diff --git a/Runtime/ScenarioContext.cs b/Runtime/ScenarioContext.cs index c4cb6a2f0..c0fd7d6b3 100644 --- a/Runtime/ScenarioContext.cs +++ b/Runtime/ScenarioContext.cs @@ -55,7 +55,17 @@ public object GetBindingInstance(Type bindingType) object value; if (!bindingInstances.TryGetValue(bindingType, out value)) { - value = Activator.CreateInstance(bindingType); + var ctors = bindingType.GetConstructors(); + if (bindingType.IsClass && ctors.Length == 0) + throw new MissingMethodException(String.Format("No public constructors found for type {0}", bindingType.FullName)); + + var parameters = new List(); + foreach (var param in ctors[0].GetParameters()) + { + parameters.Add(GetBindingInstance(param.ParameterType)); + } + + value = Activator.CreateInstance(bindingType, parameters.ToArray()); bindingInstances.Add(bindingType, value); } diff --git a/TechTalk.SpecFlow.sln b/TechTalk.SpecFlow.sln index 84db78353..3c3baa592 100644 --- a/TechTalk.SpecFlow.sln +++ b/TechTalk.SpecFlow.sln @@ -36,6 +36,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechTalk.SpecFlow.Generator EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechTalk.SpecFlow.Tools", "Tools\TechTalk.SpecFlow.Tools.csproj", "{87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FeatureTests", "Tests\FeatureTests\FeatureTests.csproj", "{3FE793A8-E662-4026-B4EC-891324073235}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -81,6 +83,10 @@ Global {87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}.Debug|Any CPU.Build.0 = Debug|Any CPU {87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}.Release|Any CPU.ActiveCfg = Release|Any CPU {87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}.Release|Any CPU.Build.0 = Release|Any CPU + {3FE793A8-E662-4026-B4EC-891324073235}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FE793A8-E662-4026-B4EC-891324073235}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FE793A8-E662-4026-B4EC-891324073235}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3FE793A8-E662-4026-B4EC-891324073235}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -90,5 +96,6 @@ Global {F6740296-282C-4A0F-941E-A8FD1B1DAC2D} = {DCE0C3C4-5BC6-4A30-86BE-3FEFF4677A01} {70376361-0BE1-478D-8EEC-47BD1C768165} = {A10B5CD6-38EC-4D7E-9D1C-2EBA8017E437} {F8FACCF0-5497-4C6B-861F-78D72FD9561B} = {A10B5CD6-38EC-4D7E-9D1C-2EBA8017E437} + {3FE793A8-E662-4026-B4EC-891324073235} = {A10B5CD6-38EC-4D7E-9D1C-2EBA8017E437} EndGlobalSection EndGlobal diff --git a/Tests/FeatureTests/ContextInjection/ContextInjection.feature b/Tests/FeatureTests/ContextInjection/ContextInjection.feature new file mode 100644 index 000000000..29e67a832 --- /dev/null +++ b/Tests/FeatureTests/ContextInjection/ContextInjection.feature @@ -0,0 +1,26 @@ +Feature: Injecting context into step specifications + As a developer + I would like to have the system automatically inject an instance of any class as defined in the constructor of a step file + So that I don't have to rely on the global shared state and can define the contexts required for each scenario. + +Scenario: Feature with no context + Given a feature which requires no context + Then everything is dandy + +Scenario: Feature with a single context + Given a feature which requires a single context + Then the context is set + +Scenario: Feature with multiple contexts + Given a feature which requires multiple contexts + Then the contexts are set + +Scenario: Feature with recursive contexts + Given a feature which requires a recursive context + Then the context is set + And its sub-context is set + +Scenario: Feature with a dependent context + Given a feature which requires a single context + Then the context is set + And the context was created by the feature with a single context scenario \ No newline at end of file diff --git a/Tests/FeatureTests/ContextInjection/ContextInjection.feature.cs b/Tests/FeatureTests/ContextInjection/ContextInjection.feature.cs new file mode 100644 index 000000000..2856da539 --- /dev/null +++ b/Tests/FeatureTests/ContextInjection/ContextInjection.feature.cs @@ -0,0 +1,109 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.21006.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace FeatureTests.ContextInjection +{ + using TechTalk.SpecFlow; + + + [NUnit.Framework.TestFixtureAttribute()] + [NUnit.Framework.DescriptionAttribute("Injecting context into step specifications")] + public partial class InjectingContextIntoStepSpecificationsFeature + { + + private static TechTalk.SpecFlow.ITestRunner testRunner; + + [NUnit.Framework.TestFixtureSetUpAttribute()] + public virtual void FeatureSetup() + { + testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); + TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en"), "Injecting context into step specifications", "As a developer\r\nI would like to have the system automatically inject an instance " + + "of any class as defined in the constructor of a step file\r\nSo that I don\'t have " + + "to rely on the global shared state and can define the contexts required for each" + + " scenario.", ((string[])(null))); + testRunner.OnFeatureStart(featureInfo); + } + + [NUnit.Framework.TestFixtureTearDownAttribute()] + public virtual void FeatureTearDown() + { + testRunner.OnFeatureEnd(); + testRunner = null; + } + + public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo) + { + testRunner.OnScenarioStart(scenarioInfo); + } + + [NUnit.Framework.TearDownAttribute()] + public virtual void ScenarioTearDown() + { + testRunner.OnScenarioEnd(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Feature with no context")] + public virtual void FeatureWithNoContext() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Feature with no context", ((string[])(null))); + this.ScenarioSetup(scenarioInfo); + testRunner.Given("a feature which requires no context"); + testRunner.Then("everything is dandy"); + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Feature with a single context")] + public virtual void FeatureWithASingleContext() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Feature with a single context", ((string[])(null))); + this.ScenarioSetup(scenarioInfo); + testRunner.Given("a feature which requires a single context"); + testRunner.Then("the context is set"); + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Feature with multiple contexts")] + public virtual void FeatureWithMultipleContexts() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Feature with multiple contexts", ((string[])(null))); + this.ScenarioSetup(scenarioInfo); + testRunner.Given("a feature which requires multiple contexts"); + testRunner.Then("the contexts are set"); + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Feature with recursive contexts")] + public virtual void FeatureWithRecursiveContexts() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Feature with recursive contexts", ((string[])(null))); + this.ScenarioSetup(scenarioInfo); + testRunner.Given("a feature which requires a recursive context"); + testRunner.Then("the context is set"); + testRunner.And("its sub-context is set"); + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Feature with a dependent context")] + public virtual void FeatureWithADependentContext() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Feature with a dependent context", ((string[])(null))); + this.ScenarioSetup(scenarioInfo); + testRunner.Given("a feature which requires a single context"); + testRunner.Then("the context is set"); + testRunner.And("the context was created by the feature with a single context scenario"); + testRunner.CollectScenarioErrors(); + } + } +} diff --git a/Tests/FeatureTests/ContextInjection/FeatureWithADependentContextSteps.cs b/Tests/FeatureTests/ContextInjection/FeatureWithADependentContextSteps.cs new file mode 100644 index 000000000..264e839c2 --- /dev/null +++ b/Tests/FeatureTests/ContextInjection/FeatureWithADependentContextSteps.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using TechTalk.SpecFlow; + +namespace FeatureTests.ContextInjection +{ + [Binding] + public class FeatureWithADependentContextSteps + { + private readonly SingleContext _context; + + public FeatureWithADependentContextSteps(SingleContext context) + { + _context = context; + } + + [Given("a feature which requires a dependent context")] + public void GivenAFeatureWhichRequiresADependentContext() + { + } + + [Then("the context was created by the feature with a single context scenario")] + public void ThenTheContextWasCreatedByTheFeatureWithASingleContextScenario() + { + Assert.That(_context.WasCreatedBy, Is.EqualTo("Feature With A Single Context")); + } + } +} diff --git a/Tests/FeatureTests/ContextInjection/FeatureWithARecursiveContextSteps.cs b/Tests/FeatureTests/ContextInjection/FeatureWithARecursiveContextSteps.cs new file mode 100644 index 000000000..17508a14f --- /dev/null +++ b/Tests/FeatureTests/ContextInjection/FeatureWithARecursiveContextSteps.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using TechTalk.SpecFlow; + +namespace FeatureTests.ContextInjection +{ + [Binding] + public class FeatureWithARecursiveContextSteps + { + private readonly NestedContext _context; + + public FeatureWithARecursiveContextSteps(NestedContext context) + { + _context = context; + } + + [Given("a feature which requires a recursive context")] + public void GivenAFeatureWhichRequiresARecursiveContext() + { + } + + [Then("its sub-context is set")] + public void ThenItsSubContextIsSet() + { + Assert.That(_context.TheNestedContext, Is.Not.Null); + } + } +} diff --git a/Tests/FeatureTests/ContextInjection/FeatureWithASingleContextSteps.cs b/Tests/FeatureTests/ContextInjection/FeatureWithASingleContextSteps.cs new file mode 100644 index 000000000..90adfaa1a --- /dev/null +++ b/Tests/FeatureTests/ContextInjection/FeatureWithASingleContextSteps.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using TechTalk.SpecFlow; + +namespace FeatureTests.ContextInjection +{ + [Binding] + public class FeatureWithASingleContextSteps + { + private readonly SingleContext _context; + + public FeatureWithASingleContextSteps(SingleContext context) + { + _context = context; + _context.WasCreatedBy = "Feature With A Single Context"; + } + + [Given("a feature which requires a single context")] + public void GivenAFeatureWhichRequiresASingleContext() + { + } + + [Then("the context is set")] + public void ThenTheContextIsSet() + { + Assert.That(_context, Is.Not.Null); + } + } +} diff --git a/Tests/FeatureTests/ContextInjection/FeatureWithMultipleContextsSteps.cs b/Tests/FeatureTests/ContextInjection/FeatureWithMultipleContextsSteps.cs new file mode 100644 index 000000000..811fb6097 --- /dev/null +++ b/Tests/FeatureTests/ContextInjection/FeatureWithMultipleContextsSteps.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using TechTalk.SpecFlow; + +namespace FeatureTests.ContextInjection +{ + [Binding] + public class FeatureWithMultipleContextsSteps + { + private readonly SingleContext _context1; + private readonly SingleContext _context2; + + public FeatureWithMultipleContextsSteps(SingleContext context1, SingleContext context2) + { + _context1 = context1; + _context2 = context2; + } + + [Given("a feature which requires multiple contexts")] + public void GivenAFeatureWhichRequiresMultipleContexts() + { + } + + [Then("the contexts are set")] + public void ThenTheContextsAreSet() + { + Assert.That(_context1, Is.Not.Null); + Assert.That(_context2, Is.Not.Null); + } + } +} diff --git a/Tests/FeatureTests/ContextInjection/FeatureWithNoContextSteps.cs b/Tests/FeatureTests/ContextInjection/FeatureWithNoContextSteps.cs new file mode 100644 index 000000000..8cebd4fea --- /dev/null +++ b/Tests/FeatureTests/ContextInjection/FeatureWithNoContextSteps.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TechTalk.SpecFlow; + +namespace FeatureTests.ContextInjection +{ + [Binding] + public class FeatureWithNoContextSteps + { + [Given("a feature which requires no context")] + public void GivenAFeatureWhichRequiresNoContext() + { + } + + [Then("everything is dandy")] + public void ThenEverythingIsDandy() + { + } + } +} diff --git a/Tests/FeatureTests/ContextInjection/NestedContext.cs b/Tests/FeatureTests/ContextInjection/NestedContext.cs new file mode 100644 index 000000000..d6485a316 --- /dev/null +++ b/Tests/FeatureTests/ContextInjection/NestedContext.cs @@ -0,0 +1,17 @@ +namespace FeatureTests.ContextInjection +{ + public class NestedContext + { + private readonly SingleContext _context; + + public NestedContext(SingleContext context) + { + _context = context; + } + + public SingleContext TheNestedContext + { + get { return _context; } + } + } +} \ No newline at end of file diff --git a/Tests/FeatureTests/ContextInjection/SingleContext.cs b/Tests/FeatureTests/ContextInjection/SingleContext.cs new file mode 100644 index 000000000..f8c73811b --- /dev/null +++ b/Tests/FeatureTests/ContextInjection/SingleContext.cs @@ -0,0 +1,7 @@ +namespace FeatureTests.ContextInjection +{ + public class SingleContext + { + public string WasCreatedBy { get; set; } + } +} \ No newline at end of file diff --git a/Tests/FeatureTests/FeatureTests.csproj b/Tests/FeatureTests/FeatureTests.csproj new file mode 100644 index 000000000..120a43809 --- /dev/null +++ b/Tests/FeatureTests/FeatureTests.csproj @@ -0,0 +1,92 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {3FE793A8-E662-4026-B4EC-891324073235} + Library + Properties + FeatureTests + FeatureTests + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\lib\nunit\nunit.framework.dll + True + + + ..\..\lib\mocking\Rhino.Mocks.dll + + + + + + + + + + + True + True + ContextInjection.feature + + + + + + + + + + + + + {7CCEF6D6-FC17-422E-9BED-EDD752B6496F} + TechTalk.SpecFlow.Parser + + + {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4} + TechTalk.SpecFlow + True + + + + + SpecFlowSingleFileGenerator + ContextInjection.feature.cs + + + SpecFlowSingleFileGenerator + ListArguments.feature.cs + + + + + \ No newline at end of file diff --git a/Tests/FeatureTests/Properties/AssemblyInfo.cs b/Tests/FeatureTests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..ec26ae4c5 --- /dev/null +++ b/Tests/FeatureTests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FeatureTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ParserTests")] +[assembly: AssemblyCopyright("Copyright © 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d5669f24-3655-4405-85c8-fa74909e84cc")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]