Coder Social home page Coder Social logo

specflow.autofac's Introduction

SpecFlow.Autofac is now part of SpecFlow!

Sources for it are here: https://github.com/techtalk/SpecFlow/tree/master/Plugins/SpecFlow.Autofac.SpecFlowPlugin Please open Issues and Pull Request in the SpecFlow repro.

SpecFlow.Autofac

SpecFlow plugin for using Autofac as a dependency injection framework for step definitions.

Currently supports

  • SpecFlow v2.1
  • Autofac v3.5.2 or above

License: Apache (https://github.com/gasparnagy/SpecFlow.Autofac/blob/master/LICENSE)

NuGet: https://www.nuget.org/packages/SpecFlow.Autofac

See my blog post (http://gasparnagy.com/2016/08/specflow-tips-customizing-dependency-injection-with-autofac/) for more information and background, you can also look at the complete example at https://github.com/gasparnagy/SpecFlow.Autofac/tree/master/sample/MyCalculator.

Build status

Usage

Install plugin from NuGet into your SpecFlow project.

PM> Install-Package SpecFlow.Autofac

Create a static method somewhere in the SpecFlow project (recommended to put it into the Support folder) that returns an Autofac ContainerBuilder and tag it with the [ScenarioDependencies] attribute. Configure your dependencies for the scenario execution within the method. You also have to register the step definition classes, that you can do by either registering all public types from the SpecFlow project:

builder.RegisterAssemblyTypes(typeof(YourClassInTheSpecFlowProject).Assembly).SingleInstance();

or by registering all classes marked with the [Binding] attribute:

builder.RegisterTypes(typeof(TestDependencies).Assembly.GetTypes().Where(t => Attribute.IsDefined(t, typeof(BindingAttribute))).ToArray()).SingleInstance();

A typical dependency builder method probably looks like this:

[ScenarioDependencies]
public static ContainerBuilder CreateContainerBuilder()
{
  // create container with the runtime dependencies
  var builder = Dependencies.CreateContainerBuilder();

  //TODO: add customizations, stubs required for testing

  builder.RegisterTypes(typeof(TestDependencies).Assembly.GetTypes().Where(t => Attribute.IsDefined(t, typeof(BindingAttribute))).ToArray()).SingleInstance();
  
  return builder;
}

Release History

v1.2.0

  • Support SpecFlow v3.0 (thx to @jsiegmund)

v1.1.0

  • Support SpecFlow v2.2.1 (thx to Zac Charles, @einsteine89)
  • Allow the container to resolve ScenarioContext, FeatureContext, TestThreadContext, IObjectContainer (by @djmnz)

v1.0.2

  • Reduced the minimum Autofac version to 3.5.2 to target broader audience (PR#1 by @BlackstarSolar)

v1.0.1

  • Fix missing assembly from NuGet package (build order issue)

v1.0.0

specflow.autofac's People

Contributors

djmnz avatar einsteine89 avatar gasparnagy avatar sabotageandi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

specflow.autofac's Issues

Support for multitenant dependency injection

Hi,

I'm trying to apply multitenant DI of Autofac with Specflow.

As described in autofac multitenant doc page, general usage should be like that:

var builder = new ContainerBuilder();
builder.RegisterType<Consumer>().As<IDependencyConsumer>().InstancePerDependency();
builder.RegisterType<BaseDependency>().As<IDependency>().SingleInstance();

var appContainer = builder.Build();

var tenantIdentifier = new MyTenantIdentificationStrategy();

var mtc = new MultitenantContainer(tenantIdentifier, appContainer);

// Configure the overrides for each tenant by passing in the tenant ID
// and a lambda that takes a ContainerBuilder.
mtc.ConfigureTenant('1', b => b.RegisterType<Tenant1Dependency>().As<IDependency>().InstancePerDependency());
mtc.ConfigureTenant('2', b => b.RegisterType<Tenant2Dependency>().As<IDependency>().SingleInstance());

Specflow.Autofac is used by creating a static containerbuilder method with [ScenarioDependencies] attribute. This attribute requires unbuilt container to inject dependencies. However multitenant configuration requires built one. That's why I get the exception
"System.InvalidOperationException : Build() or Update() can only be called once on a ContainerBuilder."
when I try to use above multitenant code with [ScenarioDependencies] attribute.

Is there any work around solution for this problem? Or should I write my custom DI solution instead of [ScenarioDependencies] attribute?

Plugin doesn't work when using FeatureContext on BeforeFeature Hook constructor

When you have a test using FeatureContext on the BeforeFeature constructor like:

[BeforeFeature]
        public static void BeforeFeature(FeatureContext feature)
        {
            //Testing Autofac plugin
        }

The SpecFlow.Autofac plugin will try to resolve the AutofacTestObjectResolver class BEFORE registering the CustomizeScenarioDependencies and throw an error:

Message: OneTimeSetUp: BoDi.ObjectContainerException : Interface cannot be resolved: Autofac.IComponentContext

Is there a fix for it?

"Unable to find scenario dependencies!" thrown when using an external step assembly

  1. Create a project containing Specflow step definitions.
  2. Create a test project, which uses Specflow.Autofac
  3. Reference the external step assembly create at S1, by having in App.config:
 <specFlow>
  <plugins>
      <add name="SpecFlow.Autofac" type="Runtime" />
    </plugins>
  <stepAssemblies>
    <stepAssembly assembly="StepDefinitions" />
  </stepAssemblies>
</specFlow>
  1. Run a test which uses steps from the external dependency.

Expected Result: Test runs successfully.
Actual result : Exception has been thrown by the target of an invocation.
----> System.Exception : Unable to find scenario dependencies! Mark a static method that returns a ContainerBuilder with [ScenarioDependencies]! => I already have it defined under support directory in my tests project.

Note! If I remove the step assembly dependency from App.config, the test starts, but fails with "No matching step definition found for the step. Use the following code to create one"

.Net Core with NuGet - The type or namespace name could not be found

Versions

  • SpecFlow 3.0.188
  • xUnit 2.4.1
  • VS 2017 Enterprise
  • .Net Core 2.2

Setup

  • I created a xUnit test project (.Net Core).
  • I added a simple feature file with auto-generated step mappings (ScenarioContext.Current.Pending()).
  • I added the following nuget packages (Specflow & plugins)
<ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
    <PackageReference Include="SpecFlow" Version="3.0.188" />
    <PackageReference Include="SpecFlow.Autofac" Version="1.2.0"  />
    <PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.0.188" />
    <PackageReference Include="SpecFlow.xUnit" Version="3.0.188" />
    <PackageReference Include="xunit" Version="2.4.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
</ItemGroup>
  • I added a simple Support class for the container builder definition
using Autofac;
using SpecFlow.Autofac;
using TechTalk.SpecFlow;

[Binding]
class SpecDependancies
{
	[ScenarioDependencies]
	public static ContainerBuilder CreateContainerBuilder()
	{
		return new ContainerBuilder();
	}
}

Issue Description

I have an error on [ScenarioDependencies] and on using Specflow.Autofac; telling me that
The type or namespace name could not be found.

Investigation & workaround

If I open the NuGet tree in Visual Studio, under the Specflow.Autofac, the SpecFlow.Autofac.SpecFlowPlugin.dll isn't there. Which is different if I check under the SpecFlow.xUnit package since the TechTalk.SpecFlow.xUnit.SpecFlow.dll appears and it also is copied in the output folder.

I looked around the Specflow plugin documentation and the NuGet samples seems quite different from the plugin, I can't say if this could be a breaking change with v3... I'm still new in creating NuGet packages.

It works fine though, if I add a strict reference to the SpecFlow.Autofac.SpecFlowPlugin.dll, so the plugin should be good, but NuGet helps a lot in managing external dependencies.

Support SpecFlow 2.2

We have issues using SpecFlow 2.1 which have been resovled in SpecFlow 2.2. PLEASE can you address this issue... System.Typeload Exception: Could not load type TechTalk.Specflow.Infrastructre.IBindingInstanceResolver from assembly TechTalk.Specflow Version 2.2.0.0

Cannot resolve ScenarioInfo

Hi,

I have a step class where I would like to inject the current ScenarioInfo instance in order to retrieve the test tile. The problem is that if I add ScenarioInfo as a constructor parameter, the test fails with:

Test method MyCalculator.Specs.Features.AdditionFeature.AddTwoNumbers threw exception: 
Autofac.Core.DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'MyCalculator.Specs.StepDefinitions.CalculatorSteps' can be invoked with the available services and parameters:
Cannot resolve parameter 'TechTalk.SpecFlow.ScenarioInfo scenarioInfo' of constructor 'Void .ctor(MyCalculator.ICalculator, TechTalk.SpecFlow.ScenarioInfo)'.

How can I use ScenarioInfo, without using the static ScenarioContext.Current?

Thanks

Add [FeatureDependencies] attribute

Hi,

We successfully added Specflow.Autofac to our project, but quite a few times we felt the need of an [FeatureDependencies] attribute to inject some dependencies at the feature level instead of scenario.

I'm not sure about the impact and how complex this change would be, but I would definitely want to see if someone else had the same issue.

Thank you and happy coding!

Static access to built IContainer

In a custom DI solution using Autofac, with [ScenarioDependencies] as "entry point" for Autofac container builder, it'd be a very useful feature to have access to built IContainer in order to easily and manually resolve dependencies where needed.
Currently, we're forced to use a constructor injection, which is a limitation in some use cases.

This would also solve the issue some users had with the multitenant setting, as it requires access to built IContainer.

@gasparnagy thank you for your hard work and let me know your thoughts. It's a simple requirement, which can be easily deployed on the official NuGet package and I'd rather use the official one than build my own.

Specflow.Autofac with Specflow 2.2

Does Specflow.Autofac works with Specflow 2.2?

When I try to run tests I get:

OneTimeSetUp: System.TypeLoadException : Could not load type 'TechTalk.SpecFlow.Infrastructure.IBindingInstanceResolver' from assembly 'TechTalk.SpecFlow, Version=2.2.0.0, Culture=neutral, PublicKeyToken=0778194805d6db41'.

With Specflow 2.1 works fine. Is there a way to solve this?

Thanks!

Specflow 2.2

Hi

I wanted to ask if is there any plan to make it work with Specflow 2.2?

Right now, even using the pull request from @zaccharles (#7) I still face some issues when my BeforeFeature hook has FeatureContext as parameter to be resolved.

Basically it goes to ResolveBindingInstance before registering scenario/feature in runtimePluginEvents.CustomizeScenarioDependencies

That only happens if you are using 2.2 Specflowchanges (Like I mentioned, BeforeFeature with feature context as parameter)

Thanks!

Lifetime is Incorrect

#5 is related to this. This plugin totally fails to provide proper lifetime and scope semantics. For every test a new container is created. This will be horribly inefficient for any complex system of component registrations (and may actually cause problems in some cases?). The container should be created only once, and a new lifetime scope should be created for each scenario (and should be properly disposed after the scenario). Honestly, I don't know if this is even possible with the existing plugin architecture? Between this and the lack of support for ScenarioContext and ScenarioInfo I'm not sure if I can even use this plugin.

Registered dependencies are not disposed

Hi,

If I register types that implement IDisposable in the Autofac Container, these are not disposed after the scenario has run. This works if I use the BoDI container.

Thanks.

[Question] Thread-safe ScenarioContext registration

Hi,

I am trying to enable running tests in parallel, but I cannot seem to make it work.

I had an AfterScenario hook which initially was verifying the static ScenarioContext.Current.TestError value.
All the DI registration is made using Specflow.Autofac package.

After upgrading NUnit to version 3.6.1, it threw an exception complaining about the use of this static ScenarioContext with a link to http://specflow.org/documentation/Parallel-Execution/

I made the update in my step class as in above link:
`
[Binding]
public class StepsWithScenarioContext
{
private readonly ScenarioContext scenarioContext;

public StepsWithScenarioContext(ScenarioContext scenarioContext)
{
    if (scenarioContext == null) throw new ArgumentNullException("scenarioContext");
    this.scenarioContext = scenarioContext;
}

[AfterScenario]
public void AfterScenario()
{
     if (_scenarioContext.TestError != null)  
            //do something
}

}
`

But now, the below exception is thrown:
Cannot resolve parameter 'TechTalk.SpecFlow.ScenarioContext scenarioContext'

I tried to find a way to register this scenario context into my DI, but wasn't able to find a way.
Did anyone else had this issue?
Is there a way to register the ScenarioContext into Specflow.Autofac registration?

Thank you and happy coding!

Tests fail when running in parallel

I am trying to run tests in parallel using the NUnit Parallelizable attribute (http://www.specflow.org/documentation/Parallel-Execution/). When my tests run serially they all work fine. Running them in parallel causes the following error on some tests:

System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
  ----> System.ArgumentException : An item with the same key has already been added.
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Delegate.DynamicInvokeImpl(Object[] args)
   at BoDi.ObjectContainer.InvokeFactoryDelegate(Delegate factoryDelegate, ResolutionList resolutionPath, RegistrationKey keyToResolve)
   at BoDi.ObjectContainer.FactoryRegistration.Resolve(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath)
   at BoDi.ObjectContainer.ResolveObject(RegistrationKey keyToResolve, ResolutionList resolutionPath)
   at BoDi.ObjectContainer.Resolve(Type typeToResolve, ResolutionList resolutionPath, String name)
   at BoDi.ObjectContainer.Resolve[T](String name)
   at SpecFlow.Autofac.AutofacBindingInstanceResolver.ResolveBindingInstance(Type bindingType, IObjectContainer scenarioContainer)
   at lambda_method(Closure , IContextManager )
   at TechTalk.SpecFlow.Bindings.BindingInvoker.InvokeBinding(IBinding binding, IContextManager contextManager, Object[] arguments, ITestTracer testTracer, TimeSpan& duration)
   at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.FireEvents(HookType bindingEvent)
   at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnScenarioStart(ScenarioInfo scenarioInfo)
   at Dyson.Jda.AutomatedTesting.Features.AwardEntrantSignInFeature.ScenarioSetup(ScenarioInfo scenarioInfo)
   at Dyson.Jda.AutomatedTesting.Features.AwardEntrantSignInFeature.AwardEntrantCannotSignInWithInvalidCredentials(String email, String password, String[] exampleTags) in C:\Code\Git\james-dyson-award\JDA\AutomatedTests\Dyson.Jda.AutomatedTesting\Features\AwardEntrantSignIn.feature:line 32
--ArgumentException
   at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at BoDi.ObjectContainer.TypeRegistration.Resolve(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath)
   at BoDi.ObjectContainer.ResolveObject(RegistrationKey keyToResolve, ResolutionList resolutionPath)
   at BoDi.ObjectContainer.Resolve(Type typeToResolve, ResolutionList resolutionPath, String name)
   at BoDi.ObjectContainer.Resolve[T](String name)
   at SpecFlow.Autofac.AutofacPlugin.<>c__DisplayClass0_0.<Initialize>b__2()

BoDi by itself supports running tests in parallel and I have done this successfully on another project. Is it supported by this plugin?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.