Skip to content

Commit

Permalink
Fix XUnit ITestOutputhelper and MSTest TestContext container registra…
Browse files Browse the repository at this point in the history
…tions so that they are available in BeforeScenario hooks. (#1093)

Also added NUnit current TestContext scenario container registration.
See #936
  • Loading branch information
alexvv authored and SabotageAndi committed Jul 10, 2018
1 parent 8de7eaa commit b052287
Show file tree
Hide file tree
Showing 20 changed files with 630 additions and 520 deletions.
18 changes: 17 additions & 1 deletion TechTalk.SpecFlow.Generator/TestClassGenerationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class TestClassGenerationContext
public CodeMemberMethod TestInitializeMethod { get; private set; }
public CodeMemberMethod TestCleanupMethod { get; private set; }
public CodeMemberMethod ScenarioInitializeMethod { get; private set; }
public CodeMemberMethod ScenarioStartMethod { get; private set; }
public CodeMemberMethod ScenarioCleanupMethod { get; private set; }
public CodeMemberMethod FeatureBackgroundMethod { get; private set; }
public CodeMemberField TestRunnerField { get; private set; }
Expand All @@ -27,7 +28,21 @@ public class TestClassGenerationContext

public IDictionary<string, object> CustomData { get; private set; }

public TestClassGenerationContext(IUnitTestGeneratorProvider unitTestGeneratorProvider, SpecFlowDocument document, CodeNamespace ns, CodeTypeDeclaration testClass, CodeMemberField testRunnerField, CodeMemberMethod testClassInitializeMethod, CodeMemberMethod testClassCleanupMethod, CodeMemberMethod testInitializeMethod, CodeMemberMethod testCleanupMethod, CodeMemberMethod scenarioInitializeMethod, CodeMemberMethod scenarioCleanupMethod, CodeMemberMethod featureBackgroundMethod, bool generateRowTests)
public TestClassGenerationContext(
IUnitTestGeneratorProvider unitTestGeneratorProvider,
SpecFlowDocument document,
CodeNamespace ns,
CodeTypeDeclaration testClass,
CodeMemberField testRunnerField,
CodeMemberMethod testClassInitializeMethod,
CodeMemberMethod testClassCleanupMethod,
CodeMemberMethod testInitializeMethod,
CodeMemberMethod testCleanupMethod,
CodeMemberMethod scenarioInitializeMethod,
CodeMemberMethod scenarioStartMethod,
CodeMemberMethod scenarioCleanupMethod,
CodeMemberMethod featureBackgroundMethod,
bool generateRowTests)
{
UnitTestGeneratorProvider = unitTestGeneratorProvider;
Document = document;
Expand All @@ -39,6 +54,7 @@ public TestClassGenerationContext(IUnitTestGeneratorProvider unitTestGeneratorPr
TestInitializeMethod = testInitializeMethod;
TestCleanupMethod = testCleanupMethod;
ScenarioInitializeMethod = scenarioInitializeMethod;
ScenarioStartMethod = scenarioStartMethod;
ScenarioCleanupMethod = scenarioCleanupMethod;
FeatureBackgroundMethod = featureBackgroundMethod;
GenerateRowTests = generateRowTests;
Expand Down
41 changes: 31 additions & 10 deletions TechTalk.SpecFlow.Generator/UnitTestFeatureGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
using System.Reflection;
using System.Text.RegularExpressions;
using Gherkin.Ast;
using TechTalk.SpecFlow.Configuration;
using TechTalk.SpecFlow.Generator.Configuration;
using TechTalk.SpecFlow.Generator.UnitTestConverter;
using TechTalk.SpecFlow.Generator.UnitTestProvider;
using TechTalk.SpecFlow.Infrastructure;
using TechTalk.SpecFlow.Parser;
using TechTalk.SpecFlow.Tracing;
using TechTalk.SpecFlow.Utils;
Expand All @@ -23,7 +22,8 @@ public class UnitTestFeatureGenerator : IFeatureGenerator
const string TESTCLASS_NAME_FORMAT = "{0}Feature";
const string TEST_NAME_FORMAT = "{0}";
private const string IGNORE_TAG = "@Ignore";
private const string SCENARIO_INITIALIZE_NAME = "ScenarioSetup";
private const string SCENARIO_INITIALIZE_NAME = "ScenarioInitialize";
private const string SCENARIO_START_NAME = "ScenarioStart";
private const string SCENARIO_CLEANUP_NAME = "ScenarioCleanup";
private const string TEST_INITIALIZE_NAME = "TestInitialize";
private const string TEST_CLEANUP_NAME = "ScenarioTearDown";
Expand Down Expand Up @@ -76,8 +76,9 @@ private TestClassGenerationContext CreateTestClassStructure(CodeNamespace codeNa
CreateMethod(testClass),
CreateMethod(testClass),
CreateMethod(testClass),
CreateMethod(testClass),
HasFeatureBackground(document.SpecFlowFeature) ? CreateMethod(testClass) : null,
generateRowTests: testGeneratorProvider.GetTraits().HasFlag(UnitTestGeneratorTraits.RowTests) && _specFlowConfiguration.AllowRowTests);
testGeneratorProvider.GetTraits().HasFlag(UnitTestGeneratorTraits.RowTests) && _specFlowConfiguration.AllowRowTests);
}

private CodeNamespace CreateNamespace(string targetNamespace)
Expand All @@ -102,7 +103,7 @@ private CodeNamespace CreateNamespace(string targetNamespace)

public CodeNamespace GenerateUnitTestFixture(SpecFlowDocument document, string testClassName, string targetNamespace)
{
CodeNamespace codeNamespace = CreateNamespace(targetNamespace);
var codeNamespace = CreateNamespace(targetNamespace);
var feature = document.SpecFlowFeature;

testClassName = testClassName ?? string.Format(TESTCLASS_NAME_FORMAT, feature.Name.ToIdentifier());
Expand All @@ -113,13 +114,13 @@ public CodeNamespace GenerateUnitTestFixture(SpecFlowDocument document, string t
SetupTestClassCleanupMethod(generationContext);

SetupScenarioInitializeMethod(generationContext);
SetupScenarioStartMethod(generationContext);
SetupFeatureBackground(generationContext);
SetupScenarioCleanupMethod(generationContext);

SetupTestInitializeMethod(generationContext);
SetupTestCleanupMethod(generationContext);


foreach (var scenarioDefinition in feature.ScenarioDefinitions)
{
if (string.IsNullOrEmpty(scenarioDefinition.Name))
Expand All @@ -132,7 +133,7 @@ public CodeNamespace GenerateUnitTestFixture(SpecFlowDocument document, string t
GenerateTest(generationContext, (Scenario)scenarioDefinition);
}

//before return the generated code, call generate provider's method in case the provider want to customerize the generated code
//before returning the generated code, call the provider's method in case the generated code needs to be customized
testGeneratorProvider.FinalizeTestClass(generationContext);
return codeNamespace;
}
Expand Down Expand Up @@ -299,22 +300,37 @@ private void SetupTestCleanupMethod(TestClassGenerationContext generationContext

private void SetupScenarioInitializeMethod(TestClassGenerationContext generationContext)
{
CodeMemberMethod scenarioInitializeMethod = generationContext.ScenarioInitializeMethod;
var scenarioInitializeMethod = generationContext.ScenarioInitializeMethod;

scenarioInitializeMethod.Attributes = MemberAttributes.Public;
scenarioInitializeMethod.Name = SCENARIO_INITIALIZE_NAME;
scenarioInitializeMethod.Parameters.Add(
new CodeParameterDeclarationExpression(typeof(ScenarioInfo), "scenarioInfo"));

//testRunner.OnScenarioStart(scenarioInfo);
//testRunner.OnScenarioInitialize(scenarioInfo);
var testRunnerField = GetTestRunnerExpression();
scenarioInitializeMethod.Statements.Add(
new CodeMethodInvokeExpression(
testRunnerField,
"OnScenarioStart",
nameof(ITestExecutionEngine.OnScenarioInitialize),
new CodeVariableReferenceExpression("scenarioInfo")));
}

private void SetupScenarioStartMethod(TestClassGenerationContext generationContext)
{
var scenarioStartMethod = generationContext.ScenarioStartMethod;

scenarioStartMethod.Attributes = MemberAttributes.Public;
scenarioStartMethod.Name = SCENARIO_START_NAME;

//testRunner.OnScenarioStart();
var testRunnerField = GetTestRunnerExpression();
scenarioStartMethod.Statements.Add(
new CodeMethodInvokeExpression(
testRunnerField,
nameof(ITestExecutionEngine.OnScenarioStart)));
}

private void SetupFeatureBackground(TestClassGenerationContext generationContext)
{
if (!HasFeatureBackground(generationContext.Feature))
Expand Down Expand Up @@ -549,6 +565,11 @@ private void GenerateTestBody(TestClassGenerationContext generationContext, Scen
generationContext.ScenarioInitializeMethod.Name,
new CodeVariableReferenceExpression("scenarioInfo")));

testMethod.Statements.Add(
new CodeMethodInvokeExpression(
new CodeThisReferenceExpression(),
generationContext.ScenarioStartMethod.Name));

if (HasFeatureBackground(generationContext.Feature))
{
AddLineDirective(testMethod.Statements, generationContext.Feature.Background);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Linq;
using BoDi;
using TechTalk.SpecFlow.Utils;

namespace TechTalk.SpecFlow.Generator.UnitTestProvider
Expand Down Expand Up @@ -82,26 +82,25 @@ public void SetTestClassIgnore(TestClassGenerationContext generationContext)

public virtual void FinalizeTestClass(TestClassGenerationContext generationContext)
{
// testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs<TestContext>(TestContext);
// testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs<TestContext>(_testContext);
generationContext.ScenarioInitializeMethod.Statements.Add(
new CodeMethodInvokeExpression(
new CodeMethodReferenceExpression(
new CodePropertyReferenceExpression(
new CodePropertyReferenceExpression(
new CodeFieldReferenceExpression(null, generationContext.TestRunnerField.Name),
"ScenarioContext"),
"ScenarioContainer"),
"RegisterInstanceAs",
nameof(ScenarioContext)),
nameof(ScenarioContext.ScenarioContainer)),
nameof(IObjectContainer.RegisterInstanceAs),
new CodeTypeReference(TESTCONTEXT_TYPE)),
new CodeVariableReferenceExpression(TESTCONTEXT_PROPERTY_NAME)));
new CodeVariableReferenceExpression(TESTCONTEXT_FIELD_NAME)));
}

public void SetTestClassParallelize(TestClassGenerationContext generationContext)
{
//Not Supported
}


public virtual void SetTestClassInitializeMethod(TestClassGenerationContext generationContext)
{
generationContext.TestClassInitializeMethod.Attributes |= MemberAttributes.Static;
Expand Down Expand Up @@ -201,7 +200,6 @@ public virtual void SetRow(TestClassGenerationContext generationContext, CodeMem
throw new NotSupportedException();
}


public virtual void SetTestMethodAsRow(TestClassGenerationContext generationContext, CodeMemberMethod testMethod, string scenarioTitle, string exampleSetName, string variantName, IEnumerable<KeyValuePair<string, string>> arguments)
{
if (!string.IsNullOrEmpty(exampleSetName))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Linq;
using BoDi;
using TechTalk.SpecFlow.Utils;

namespace TechTalk.SpecFlow.Generator.UnitTestProvider
Expand All @@ -18,6 +18,8 @@ public class NUnitTestGeneratorProvider : IUnitTestGeneratorProvider
protected const string TESTTEARDOWN_ATTR = "NUnit.Framework.TearDownAttribute";
protected const string IGNORE_ATTR = "NUnit.Framework.IgnoreAttribute";
protected const string DESCRIPTION_ATTR = "NUnit.Framework.DescriptionAttribute";
protected const string TESTCONTEXT_TYPE = "NUnit.Framework.TestContext";
protected const string TESTCONTEXT_INSTANCE = "NUnit.Framework.TestContext.CurrentContext";

protected CodeDomHelper CodeDomHelper { get; set; }

Expand Down Expand Up @@ -51,7 +53,18 @@ public virtual void SetTestClassIgnore(TestClassGenerationContext generationCont

public virtual void FinalizeTestClass(TestClassGenerationContext generationContext)
{
// by default, doing nothing to the final generated code
// testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs<NUnit.Framework.TestContext>(NUnit.Framework.TestContext.CurrentContext);
generationContext.ScenarioInitializeMethod.Statements.Add(
new CodeMethodInvokeExpression(
new CodeMethodReferenceExpression(
new CodePropertyReferenceExpression(
new CodePropertyReferenceExpression(
new CodeFieldReferenceExpression(null, generationContext.TestRunnerField.Name),
nameof(ScenarioContext)),
nameof(ScenarioContext.ScenarioContainer)),
nameof(IObjectContainer.RegisterInstanceAs),
new CodeTypeReference(TESTCONTEXT_TYPE)),
new CodeVariableReferenceExpression(TESTCONTEXT_INSTANCE)));
}

public virtual void SetTestClassParallelize(TestClassGenerationContext generationContext)
Expand Down Expand Up @@ -98,7 +111,6 @@ public virtual void SetTestMethodIgnore(TestClassGenerationContext generationCon
CodeDomHelper.AddAttribute(testMethod, IGNORE_ATTR);
}


public void SetRowTest(TestClassGenerationContext generationContext, CodeMemberMethod testMethod, string scenarioTitle)
{
SetTestMethod(generationContext, testMethod, scenarioTitle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,22 @@
using System.CodeDom;
using System.Collections.Generic;
using System.Linq;
using BoDi;
using TechTalk.SpecFlow.Utils;

namespace TechTalk.SpecFlow.Generator.UnitTestProvider
{
public class XUnit2TestGeneratorProvider : XUnitTestGeneratorProvider
{
private const string FEATURE_TITLE_PROPERTY_NAME = "FeatureTitle";
private const string FACT_ATTRIBUTE = "Xunit.FactAttribute";
private const string FACT_ATTRIBUTE_SKIP_PROPERTY_NAME = "Skip";
private const string THEORY_ATTRIBUTE = "Xunit.TheoryAttribute";
private const string THEORY_ATTRIBUTE_SKIP_PROPERTY_NAME = "Skip";
private new const string THEORY_ATTRIBUTE = "Xunit.TheoryAttribute";
private const string INLINEDATA_ATTRIBUTE = "Xunit.InlineDataAttribute";
private const string SKIP_REASON = "Ignored";
private const string ICLASSFIXTURE_INTERFACE = "Xunit.IClassFixture";
private const string COLLECTION_ATTRIBUTE = "Xunit.CollectionAttribute";
private const string OUTPUT_INTERFACE = "Xunit.Abstractions.ITestOutputHelper";
private const string OUTPUT_INTERFACE_PARAMETER_NAME = "testOutputHelper";
private const string OUTPUT_INTERFACE_FIELD_NAME = "_testOutputHelper";
private const string FIXTUREDATA_PARAMETER_NAME = "fixtureData";

public XUnit2TestGeneratorProvider(CodeDomHelper codeDomHelper)
:base(codeDomHelper)
{
Expand Down Expand Up @@ -62,7 +58,7 @@ public override void SetRowTest(TestClassGenerationContext generationContext, Co
return;

var args = arguments.Select(
arg => new CodeAttributeArgument(new CodePrimitiveExpression(arg))).ToList();
arg => new CodeAttributeArgument(new CodePrimitiveExpression(arg))).ToList();

args.Add(
new CodeAttributeArgument(
Expand Down Expand Up @@ -128,9 +124,9 @@ public override void FinalizeTestClass(TestClassGenerationContext generationCont
new CodePropertyReferenceExpression(
new CodePropertyReferenceExpression(
new CodeFieldReferenceExpression(null, generationContext.TestRunnerField.Name),
"ScenarioContext"),
"ScenarioContainer"),
"RegisterInstanceAs",
nameof(ScenarioContext)),
nameof(ScenarioContext.ScenarioContainer)),
nameof(IObjectContainer.RegisterInstanceAs),
new CodeTypeReference(OUTPUT_INTERFACE)),
new CodeVariableReferenceExpression(OUTPUT_INTERFACE_FIELD_NAME)));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ namespace TechTalk.SpecFlow.Generator.UnitTestProvider
{
public class XUnitTestGeneratorProvider : IUnitTestGeneratorProvider
{
private const string FEATURE_TITLE_PROPERTY_NAME = "FeatureTitle";
protected const string FEATURE_TITLE_PROPERTY_NAME = "FeatureTitle";
private const string DESCRIPTION_PROPERTY_NAME = "Description";
private const string FACT_ATTRIBUTE = "Xunit.FactAttribute";
private const string FACT_ATTRIBUTE_SKIP_PROPERTY_NAME = "Skip";
protected const string FACT_ATTRIBUTE = "Xunit.FactAttribute";
protected const string FACT_ATTRIBUTE_SKIP_PROPERTY_NAME = "Skip";
internal const string THEORY_ATTRIBUTE = "Xunit.Extensions.TheoryAttribute";
internal const string THEORY_ATTRIBUTE_SKIP_PROPERTY_NAME = "Skip";
private const string INLINEDATA_ATTRIBUTE = "Xunit.Extensions.InlineDataAttribute";
Expand Down
8 changes: 4 additions & 4 deletions TechTalk.SpecFlow/ITestRunner.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
using System;
using TechTalk.SpecFlow.Infrastructure;

namespace TechTalk.SpecFlow
{
public interface ITestRunner
Expand All @@ -16,7 +13,10 @@ public interface ITestRunner

void OnFeatureStart(FeatureInfo featureInfo);
void OnFeatureEnd();
void OnScenarioStart(ScenarioInfo scenarioInfo);

void OnScenarioInitialize(ScenarioInfo scenarioInfo);
void OnScenarioStart();

void CollectScenarioErrors();
void OnScenarioEnd();

Expand Down
7 changes: 3 additions & 4 deletions TechTalk.SpecFlow/Infrastructure/ITestExecutionEngine.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Linq;
using TechTalk.SpecFlow.Bindings;
using TechTalk.SpecFlow.Bindings;

namespace TechTalk.SpecFlow.Infrastructure
{
Expand All @@ -15,7 +13,8 @@ public interface ITestExecutionEngine
void OnFeatureStart(FeatureInfo featureInfo);
void OnFeatureEnd();

void OnScenarioStart(ScenarioInfo scenarioInfo);
void OnScenarioInitialize(ScenarioInfo scenarioInfo);
void OnScenarioStart();
void OnAfterLastStep();
void OnScenarioEnd();

Expand Down
7 changes: 5 additions & 2 deletions TechTalk.SpecFlow/Infrastructure/TestExecutionEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using TechTalk.SpecFlow.Bindings;
using TechTalk.SpecFlow.Bindings.Reflection;
using TechTalk.SpecFlow.Compatibility;
using TechTalk.SpecFlow.Configuration;
using TechTalk.SpecFlow.ErrorHandling;
using TechTalk.SpecFlow.Tracing;
using TechTalk.SpecFlow.UnitTestProvider;
Expand Down Expand Up @@ -123,9 +122,13 @@ public void OnFeatureEnd()
contextManager.CleanupFeatureContext();
}

public void OnScenarioStart(ScenarioInfo scenarioInfo)
public void OnScenarioInitialize(ScenarioInfo scenarioInfo)
{
contextManager.InitializeScenarioContext(scenarioInfo);
}

public void OnScenarioStart()
{
FireScenarioEvents(HookType.BeforeScenario);
}

Expand Down

0 comments on commit b052287

Please sign in to comment.