diff --git a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Framework/SpecFlowTestDiscoverer.cs b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Framework/SpecFlowTestDiscoverer.cs index e686ebf..fb79056 100644 --- a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Framework/SpecFlowTestDiscoverer.cs +++ b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Framework/SpecFlowTestDiscoverer.cs @@ -10,7 +10,7 @@ namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.Framework public class SpecFlowTestDiscoverer : XunitTestFrameworkDiscoverer { public SpecFlowTestDiscoverer(IAssemblyInfo assemblyInfo, ISourceInformationProvider sourceProvider, IMessageSink diagnosticMessageSink, IXunitTestCollectionFactory collectionFactory = null) : - base(CreateSpecFlowProjectAssemblyInfo(assemblyInfo), sourceProvider, diagnosticMessageSink) + base(CreateSpecFlowProjectAssemblyInfo(assemblyInfo), sourceProvider, diagnosticMessageSink, collectionFactory) { } @@ -21,18 +21,21 @@ private static SpecFlowProjectAssemblyInfo CreateSpecFlowProjectAssemblyInfo(IAs return new SpecFlowProjectAssemblyInfo(assemblyInfo); } - private bool IsSpecFlowTest(object test) + private bool IsSpecFlowTypeInfo(ITypeInfo typeInfo) { - return test is SpecFlowFeatureTestClass; + return typeInfo is SpecFlowFeatureTypeInfo; + } + + private bool IsSpecFlowTest(ITestClass testClass) + { + return testClass is SpecFlowFeatureTestClass; } protected override ITestClass CreateTestClass(ITypeInfo typeInfo) { - if (IsSpecFlowTest(typeInfo)) + if (IsSpecFlowTypeInfo(typeInfo)) { - //TODO: return (ITestClass)new SpecFlowTestClass(this.TestCollectionFactory.Get(typeInfo), typeInfo); - ((SpecFlowFeatureTestClass)typeInfo).Hack_SetTestCollection(this.TestCollectionFactory.Get(typeInfo)); - return (ITestClass) typeInfo; + return new SpecFlowFeatureTestClass(TestCollectionFactory.Get(typeInfo), typeInfo); } return base.CreateTestClass(typeInfo); } @@ -43,11 +46,11 @@ protected override bool FindTestsForType(ITestClass testClass, bool includeSourc if (!IsSpecFlowTest(testClass)) return base.FindTestsForType(testClass, includeSourceInformation, messageBus, discoveryOptions); - var featureTestClass = (SpecFlowFeatureTestClass)testClass; - var gherkinDocument = featureTestClass.GetDocument(); + var featureTestClass = ((SpecFlowFeatureTestClass)testClass); + var gherkinDocument = featureTestClass.FeatureTypeInfo.GetDocument(); if (gherkinDocument?.SpecFlowFeature != null) { - featureTestClass.FeatureName = gherkinDocument.SpecFlowFeature.Name; + featureTestClass.FeatureTypeInfo.FeatureName = gherkinDocument.SpecFlowFeature.Name; var featureTags = gherkinDocument.SpecFlowFeature.Tags.GetTags().ToArray(); foreach (var scenarioDefinition in gherkinDocument.SpecFlowFeature.ScenarioDefinitions.Where(sd => !(sd is Background))) { diff --git a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Framework/SpecFlowTestFramework.cs b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Framework/SpecFlowTestFramework.cs index 55c6a53..58e3344 100644 --- a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Framework/SpecFlowTestFramework.cs +++ b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Framework/SpecFlowTestFramework.cs @@ -2,6 +2,7 @@ using Xunit.Abstractions; using Xunit.Sdk; +// ReSharper disable once CheckNamespace namespace SpecFlow.xUnitAdapter.SpecFlowPlugin { public class SpecFlowTestFramework : XunitTestFramework @@ -10,14 +11,10 @@ public SpecFlowTestFramework(IMessageSink diagnosticMessageSink) : base(diagnost { } + // we only override the discoverer, we can use the built-in executor as it is anyway delegates the actual exectution to the test cases (ScenarioTestCase) protected override ITestFrameworkDiscoverer CreateDiscoverer(IAssemblyInfo assemblyInfo) { return new SpecFlowTestDiscoverer(assemblyInfo, SourceInformationProvider, DiagnosticMessageSink); } - - //protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName) - //{ - // return new SpecFlowTestFrameworkExecutor(assemblyName, SourceInformationProvider, DiagnosticMessageSink); - //} } -} \ No newline at end of file +} diff --git a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Runners/ScenarioTestCaseRunner.cs b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Runners/ScenarioTestCaseRunner.cs index 6d5be9b..03c3a6f 100644 --- a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Runners/ScenarioTestCaseRunner.cs +++ b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/Runners/ScenarioTestCaseRunner.cs @@ -30,7 +30,7 @@ public void FeatureSetup(GherkinDocument gherkinDocument) Debug.Assert(gherkinDocument.Feature != null); var feature = gherkinDocument.Feature; - var assembly = Assembly.LoadFrom(TestCase.FeatureFile.SpecFlowProject.AssemblyPath); + var assembly = Assembly.LoadFrom(TestCase.FeatureTypeInfo.SpecFlowProject.AssemblyPath); testRunner = TestRunnerManager.GetTestRunner(assembly); var featureInfo = new FeatureInfo(GetFeatureCulture(feature.Language), feature.Name, feature.Description, ProgrammingLanguage.CSharp, feature.Tags.GetTags().ToArray()); testRunner.OnFeatureStart(featureInfo); @@ -79,7 +79,7 @@ protected override async Task RunTestAsync() var summary = new RunSummary() { Total = 1 }; string output = ""; - var gherkinDocument = await this.TestCase.FeatureFile.GetDocumentAsync(); + var gherkinDocument = await this.TestCase.FeatureTypeInfo.GetDocumentAsync(); Scenario scenario = null; diff --git a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/SpecFlow.xUnitAdapter.SpecFlowPlugin.csproj b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/SpecFlow.xUnitAdapter.SpecFlowPlugin.csproj index f67222d..7a2b095 100644 --- a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/SpecFlow.xUnitAdapter.SpecFlowPlugin.csproj +++ b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/SpecFlow.xUnitAdapter.SpecFlowPlugin.csproj @@ -79,8 +79,9 @@ - - + + + diff --git a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/EmbeddedFeatureFileTestClass.cs b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/EmbeddedFeatureTypeInfo.cs similarity index 84% rename from src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/EmbeddedFeatureFileTestClass.cs rename to src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/EmbeddedFeatureTypeInfo.cs index b0c5fcb..52963c0 100644 --- a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/EmbeddedFeatureFileTestClass.cs +++ b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/EmbeddedFeatureTypeInfo.cs @@ -5,14 +5,13 @@ namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.TestArtifacts { - public class EmbeddedFeatureFileTestClass : SpecFlowFeatureTestClass + public class EmbeddedFeatureTypeInfo : SpecFlowFeatureTypeInfo { - public EmbeddedFeatureFileTestClass() - : base() + public EmbeddedFeatureTypeInfo() { } - public EmbeddedFeatureFileTestClass(SpecFlowProjectAssemblyInfo specFlowProject, string resourceName) + public EmbeddedFeatureTypeInfo(SpecFlowProjectAssemblyInfo specFlowProject, string resourceName) : base(specFlowProject, resourceName) { } diff --git a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/FeatureFileTestClass.cs b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/FeatureFileTypeInfo.cs similarity index 84% rename from src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/FeatureFileTestClass.cs rename to src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/FeatureFileTypeInfo.cs index bccaec4..5f8784a 100644 --- a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/FeatureFileTestClass.cs +++ b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/FeatureFileTypeInfo.cs @@ -5,13 +5,13 @@ namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.TestArtifacts { - public class FeatureFileTestClass : SpecFlowFeatureTestClass + public class FeatureFileTypeInfo : SpecFlowFeatureTypeInfo { - public FeatureFileTestClass() + public FeatureFileTypeInfo() { } - public FeatureFileTestClass(SpecFlowProjectAssemblyInfo specFlowProject, string relativePath) + public FeatureFileTypeInfo(SpecFlowProjectAssemblyInfo specFlowProject, string relativePath) : base(specFlowProject, relativePath) { } diff --git a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/ScenarioTestCase.cs b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/ScenarioTestCase.cs index 24d59f7..0fce733 100644 --- a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/ScenarioTestCase.cs +++ b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/ScenarioTestCase.cs @@ -14,11 +14,12 @@ namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.TestArtifacts { public class ScenarioTestCase : LongLivedMarshalByRefObject, ITestMethod, IXunitTestCase, IReflectionMethodInfo { - public SpecFlowFeatureTestClass FeatureFile { get; private set; } + public SpecFlowFeatureTestClass FeatureTestClass { get; private set; } + public SpecFlowFeatureTypeInfo FeatureTypeInfo => FeatureTestClass.FeatureTypeInfo; public string Name { get; private set; } public string DisplayName => GetDisplayName(); - public string UniqueID => $"{FeatureFile.RelativePath};{Name};{ExampleId}"; + public string UniqueID => $"{FeatureTypeInfo.RelativePath};{Name};{ExampleId}"; public ISourceInformation SourceInformation { get; set; } public string SkipReason { get; set; } @@ -29,7 +30,7 @@ public class ScenarioTestCase : LongLivedMarshalByRefObject, ITestMethod, IXunit public bool IsScenarioOutline => !string.IsNullOrEmpty(ExampleId); object[] ITestCase.TestMethodArguments => ScenarioOutlineParameters?.Cast().ToArray(); - public ITestClass TestClass => FeatureFile; + public ITestClass TestClass => FeatureTestClass; public ITestMethod TestMethod => this; public IMethodInfo Method => this; @@ -40,9 +41,9 @@ public ScenarioTestCase() private ScenarioTestCase(SpecFlowFeatureTestClass featureTestClass, ScenarioDefinition scenario, string[] featureTags, Location location) { - FeatureFile = featureTestClass; + FeatureTestClass = featureTestClass; Name = scenario.Name; - SourceInformation = new SourceInformation { FileName = featureTestClass.FeatureFilePath, LineNumber = location?.Line }; + SourceInformation = new SourceInformation { FileName = FeatureTypeInfo.FeatureFilePath, LineNumber = location?.Line }; Traits = new Dictionary>(); Traits.Add("Category", featureTags.Concat(((IHasTags)scenario).Tags.GetTags()).ToList()); } @@ -70,14 +71,14 @@ private string GetDisplayName() public void Deserialize(IXunitSerializationInfo data) { - FeatureFile = data.GetValue("FeatureFile"); + FeatureTestClass = data.GetValue("FeatureTestClass"); Name = data.GetValue("Name"); ExampleId = data.GetValue("ExampleId"); } public void Serialize(IXunitSerializationInfo data) { - data.AddValue("FeatureFile", FeatureFile); + data.AddValue("FeatureTestClass", FeatureTestClass); data.AddValue("Name", Name); data.AddValue("ExampleId", ExampleId); } diff --git a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/SpecFlowFeatureTestClass.cs b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/SpecFlowFeatureTestClass.cs index 2a44af4..9061afe 100644 --- a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/SpecFlowFeatureTestClass.cs +++ b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/SpecFlowFeatureTestClass.cs @@ -1,84 +1,48 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using TechTalk.SpecFlow.Parser; +using System; using Xunit; using Xunit.Abstractions; -using Xunit.Sdk; namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.TestArtifacts { - public abstract class SpecFlowFeatureTestClass : LongLivedMarshalByRefObject, ITypeInfo, IReflectionTypeInfo, ITestClass + public class SpecFlowFeatureTestClass : LongLivedMarshalByRefObject, ITestClass { - public string FeatureName { get; set; } - public string RelativePath { get; private set; } - public SpecFlowProjectAssemblyInfo SpecFlowProject { get; private set; } - - public virtual string FeatureFilePath { get; protected set; } - - IAssemblyInfo ITypeInfo.Assembly => SpecFlowProject; - string ITypeInfo.Name => (FeatureName ?? RelativePath).Replace(".", ""); - Type IReflectionTypeInfo.Type { get { return typeof(SpecFlowGenericFixtureType); } } - - public ITypeInfo Class => this; + public ITypeInfo Class { get; private set; } public ITestCollection TestCollection { get; private set; } - #region ITypeInfo default implementation - IEnumerable ITypeInfo.GetCustomAttributes(string assemblyQualifiedAttributeTypeName) => Enumerable.Empty(); - IEnumerable ITypeInfo.GetGenericArguments() => Enumerable.Empty(); - IMethodInfo ITypeInfo.GetMethod(string methodName, bool includePrivateMethod) => null; - IEnumerable ITypeInfo.GetMethods(bool includePrivateMethods) => Enumerable.Empty(); - ITypeInfo ITypeInfo.BaseType => null; - IEnumerable ITypeInfo.Interfaces => Enumerable.Empty(); - bool ITypeInfo.IsAbstract => false; - bool ITypeInfo.IsGenericParameter => false; - bool ITypeInfo.IsGenericType => false; - bool ITypeInfo.IsSealed => false; - bool ITypeInfo.IsValueType => false; - #endregion - - protected SpecFlowFeatureTestClass() { } + public SpecFlowFeatureTypeInfo FeatureTypeInfo => (SpecFlowFeatureTypeInfo)Class; - protected SpecFlowFeatureTestClass(SpecFlowProjectAssemblyInfo specFlowProject, string relativePath) + [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] + public SpecFlowFeatureTestClass() { - SpecFlowProject = specFlowProject; - RelativePath = relativePath; } - internal void Hack_SetTestCollection(ITestCollection testCollection) + public SpecFlowFeatureTestClass(ITestCollection testCollection, ITypeInfo typeInfo) { - TestCollection = testCollection; - } - - protected ISpecFlowSourceMapper SpecFlowSourceMapper { get; } = new SpecFlowSourceMapperV1(); + if (!(typeInfo is SpecFlowFeatureTypeInfo)) + throw new ArgumentException($"Must me an instance of {nameof(SpecFlowFeatureTypeInfo)}", "typeInfo"); - public virtual void Deserialize(IXunitSerializationInfo data) - { - SpecFlowProject = data.GetValue("SpecFlowProject"); - RelativePath = data.GetValue("RelativePath"); - TestCollection = data.GetValue("TestCollection"); + TestCollection = testCollection; + Class = typeInfo; } - public virtual void Serialize(IXunitSerializationInfo data) + public void Serialize(IXunitSerializationInfo info) { - data.AddValue("SpecFlowProject", SpecFlowProject); - data.AddValue("RelativePath", RelativePath); - data.AddValue("TestCollection", TestCollection); + info.AddValue("TestCollection", TestCollection); + info.AddValue("SpecFlowProject", FeatureTypeInfo.SpecFlowProject); + info.AddValue("RelativePath", FeatureTypeInfo.RelativePath); + info.AddValue("TypeInfoKind", Class.GetType().Name); } - protected SpecFlowDocument ParseDocument(string content, string path, SpecFlowGherkinParser parser) + public void Deserialize(IXunitSerializationInfo info) { - var sourceMap = this.SpecFlowSourceMapper.ReadSourceMap(content); - - this.FeatureFilePath = sourceMap?.SourcePath ?? path; - - return parser.Parse(new StringReader(content), this.FeatureFilePath); + TestCollection = info.GetValue("TestCollection"); + var specFlowProject = info.GetValue("SpecFlowProject"); + var relativePath = info.GetValue("RelativePath"); + var typeInfoKind = info.GetValue("TypeInfoKind"); + if (typeInfoKind == nameof(FeatureFileTypeInfo)) + Class = new FeatureFileTypeInfo(specFlowProject, relativePath); + else if (typeInfoKind == nameof(EmbeddedFeatureTypeInfo)) + Class = new EmbeddedFeatureTypeInfo(specFlowProject, relativePath); } - - public abstract SpecFlowDocument GetDocument(); - - public abstract Task GetDocumentAsync(); } } diff --git a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/SpecFlowFeatureTypeInfo.cs b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/SpecFlowFeatureTypeInfo.cs new file mode 100644 index 0000000..934ec80 --- /dev/null +++ b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/SpecFlowFeatureTypeInfo.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using TechTalk.SpecFlow.Parser; +using Xunit; +using Xunit.Abstractions; + +namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.TestArtifacts +{ + public abstract class SpecFlowFeatureTypeInfo : LongLivedMarshalByRefObject, ITypeInfo, IReflectionTypeInfo + { + public string FeatureName { get; set; } + public string RelativePath { get; } + public SpecFlowProjectAssemblyInfo SpecFlowProject { get; } + + public virtual string FeatureFilePath { get; protected set; } + + IAssemblyInfo ITypeInfo.Assembly => SpecFlowProject; + string ITypeInfo.Name => (FeatureName ?? RelativePath).Replace(".", ""); + Type IReflectionTypeInfo.Type => typeof(SpecFlowGenericFixtureType); + + #region ITypeInfo default implementation + IEnumerable ITypeInfo.GetCustomAttributes(string assemblyQualifiedAttributeTypeName) => Enumerable.Empty(); + IEnumerable ITypeInfo.GetGenericArguments() => Enumerable.Empty(); + IMethodInfo ITypeInfo.GetMethod(string methodName, bool includePrivateMethod) => null; + IEnumerable ITypeInfo.GetMethods(bool includePrivateMethods) => Enumerable.Empty(); + ITypeInfo ITypeInfo.BaseType => null; + IEnumerable ITypeInfo.Interfaces => Enumerable.Empty(); + bool ITypeInfo.IsAbstract => false; + bool ITypeInfo.IsGenericParameter => false; + bool ITypeInfo.IsGenericType => false; + bool ITypeInfo.IsSealed => false; + bool ITypeInfo.IsValueType => false; + #endregion + + protected SpecFlowFeatureTypeInfo() { } + + protected SpecFlowFeatureTypeInfo(SpecFlowProjectAssemblyInfo specFlowProject, string relativePath) + { + SpecFlowProject = specFlowProject; + RelativePath = relativePath; + } + + protected ISpecFlowSourceMapper SpecFlowSourceMapper { get; } = new SpecFlowSourceMapperV1(); + + protected SpecFlowDocument ParseDocument(string content, string path, SpecFlowGherkinParser parser) + { + var sourceMap = this.SpecFlowSourceMapper.ReadSourceMap(content); + + this.FeatureFilePath = sourceMap?.SourcePath ?? path; + + return parser.Parse(new StringReader(content), this.FeatureFilePath); + } + + public abstract SpecFlowDocument GetDocument(); + + public abstract Task GetDocumentAsync(); + } +} diff --git a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/SpecFlowProjectAssemblyInfo.cs b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/SpecFlowProjectAssemblyInfo.cs index a961118..5014751 100644 --- a/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/SpecFlowProjectAssemblyInfo.cs +++ b/src/SpecFlow.xUnitAdapter.SpecFlowPlugin/TestArtifacts/SpecFlowProjectAssemblyInfo.cs @@ -43,7 +43,7 @@ public IEnumerable GetTypes(bool includePrivateTypes) foreach (var resourceName in assembly.GetManifestResourceNames().Where(x => x.EndsWith(".feature"))) { Console.WriteLine($" {resourceName}"); - yield return new EmbeddedFeatureFileTestClass(this, resourceName); + yield return new EmbeddedFeatureTypeInfo(this, resourceName); } Console.WriteLine($" Discovering feature files from folder {FeatureFilesFolder}"); @@ -51,7 +51,7 @@ public IEnumerable GetTypes(bool includePrivateTypes) { var relativePath = featureFilePath.Substring(FeatureFilesFolder.Length).TrimStart(Path.DirectorySeparatorChar); Console.WriteLine($" {relativePath}"); - yield return new FeatureFileTestClass(this, relativePath); + yield return new FeatureFileTypeInfo(this, relativePath); } // discovering "standard" xunit tests to allow incremental transition from the generated style to the new style