Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
 (#1533)

* Add SpecFlow.Autofac from https://github.com/gasparnagy/SpecFlow.Autofac/

* generate NuGet package for SpecFlow.Autofac

* fix target framework folder
  • Loading branch information
gasparnagy authored and SabotageAndi committed May 6, 2019
1 parent d9375b1 commit 01d8c62
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 2 deletions.
98 changes: 98 additions & 0 deletions Plugins/SpecFlow.Autofac.SpecFlowPlugin/AutofacPlugin.cs
@@ -0,0 +1,98 @@
using System;
using Autofac;
using SpecFlow.Autofac;
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Infrastructure;
using TechTalk.SpecFlow.Plugins;
using TechTalk.SpecFlow.UnitTestProvider;

[assembly: RuntimePlugin(typeof(AutofacPlugin))]

namespace SpecFlow.Autofac
{
using BoDi;

using TechTalk.SpecFlow;

public class AutofacPlugin : IRuntimePlugin
{
private static Object _registrationLock = new Object();

public void Initialize(RuntimePluginEvents runtimePluginEvents, RuntimePluginParameters runtimePluginParameters, UnitTestProviderConfiguration unitTestProviderConfiguration)
{
runtimePluginEvents.CustomizeGlobalDependencies += (sender, args) =>
{
// temporary fix for CustomizeGlobalDependencies called multiple times
// see https://github.com/techtalk/SpecFlow/issues/948
if (!args.ObjectContainer.IsRegistered<IContainerBuilderFinder>())
{
// an extra lock to ensure that there are not two super fast threads re-registering the same stuff
lock (_registrationLock)
{
if (!args.ObjectContainer.IsRegistered<IContainerBuilderFinder>())
{
args.ObjectContainer.RegisterTypeAs<AutofacTestObjectResolver, ITestObjectResolver>();
args.ObjectContainer.RegisterTypeAs<ContainerBuilderFinder, IContainerBuilderFinder>();
}
}
// workaround for parallel execution issue - this should be rather a feature in BoDi?
args.ObjectContainer.Resolve<IContainerBuilderFinder>();
}
};

runtimePluginEvents.CustomizeScenarioDependencies += (sender, args) =>
{
args.ObjectContainer.RegisterFactoryAs<IComponentContext>(() =>
{
var containerBuilderFinder = args.ObjectContainer.Resolve<IContainerBuilderFinder>();
var createScenarioContainerBuilder = containerBuilderFinder.GetCreateScenarioContainerBuilder();
var containerBuilder = createScenarioContainerBuilder();
RegisterSpecflowDependecies(args.ObjectContainer, containerBuilder);
var container = containerBuilder.Build();
return container.BeginLifetimeScope();
});
};
}

/// <summary>
/// Fix for https://github.com/gasparnagy/SpecFlow.Autofac/issues/11 Cannot resolve ScenarioInfo
/// Extracted from
/// https://github.com/techtalk/SpecFlow/blob/master/TechTalk.SpecFlow/Infrastructure/ITestObjectResolver.cs
/// The test objects might be dependent on particular SpecFlow infrastructure, therefore the implemented
/// resolution logic should support resolving the following objects (from the provided SpecFlow container):
/// <see cref="ScenarioContext" />, <see cref="FeatureContext" />, <see cref="TestThreadContext" /> and
/// <see cref="IObjectContainer" /> (to be able to resolve any other SpecFlow infrastucture). So basically
/// the resolution of these classes has to be forwarded to the original container.
/// </summary>
/// <param name="objectContainer">SpecFlow DI container.</param>
/// <param name="containerBuilder">Autofac ContainerBuilder.</param>
private void RegisterSpecflowDependecies(
IObjectContainer objectContainer,
global::Autofac.ContainerBuilder containerBuilder)
{
containerBuilder.Register(ctx => objectContainer).As<IObjectContainer>();
containerBuilder.Register(
ctx =>
{
var specflowContainer = ctx.Resolve<IObjectContainer>();
var scenarioContext = specflowContainer.Resolve<ScenarioContext>();
return scenarioContext;
}).As<ScenarioContext>();
containerBuilder.Register(
ctx =>
{
var specflowContainer = ctx.Resolve<IObjectContainer>();
var scenarioContext = specflowContainer.Resolve<FeatureContext>();
return scenarioContext;
}).As<FeatureContext>();
containerBuilder.Register(
ctx =>
{
var specflowContainer = ctx.Resolve<IObjectContainer>();
var scenarioContext = specflowContainer.Resolve<TestThreadContext>();
return scenarioContext;
}).As<TestThreadContext>();
}
}
}
@@ -0,0 +1,16 @@
using System;
using Autofac;
using BoDi;
using TechTalk.SpecFlow.Infrastructure;

namespace SpecFlow.Autofac
{
public class AutofacTestObjectResolver : ITestObjectResolver
{
public object ResolveBindingInstance(Type bindingType, IObjectContainer scenarioContainer)
{
var componentContext = scenarioContainer.Resolve<IComponentContext>();
return componentContext.Resolve(bindingType);
}
}
}
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using TechTalk.SpecFlow.Bindings;
using TechTalk.SpecFlow.Bindings.Reflection;

namespace SpecFlow.Autofac
{
public static class BindingRegistryExtensions
{
public static IEnumerable<IBindingType> GetBindingTypes(this IBindingRegistry bindingRegistry)
{
return bindingRegistry.GetStepDefinitions().Cast<IBinding>()
.Concat(bindingRegistry.GetHooks().Cast<IBinding>())
.Concat(bindingRegistry.GetStepTransformations())
.Select(b => b.Method.Type)
.Distinct();
}

public static IEnumerable<Assembly> GetBindingAssemblies(this IBindingRegistry bindingRegistry)
{
return bindingRegistry.GetBindingTypes().OfType<RuntimeBindingType>()
.Select(t => t.Type.Assembly)
.Distinct();
}
}
}
50 changes: 50 additions & 0 deletions Plugins/SpecFlow.Autofac.SpecFlowPlugin/ContainerBuilderFinder.cs
@@ -0,0 +1,50 @@
using System;
using System.Linq;
using System.Reflection;
using Autofac;
using TechTalk.SpecFlow.Bindings;

namespace SpecFlow.Autofac
{
public class ContainerBuilderFinder : IContainerBuilderFinder
{
private readonly IBindingRegistry bindingRegistry;
private readonly Lazy<Func<ContainerBuilder>> createScenarioContainerBuilder;

public ContainerBuilderFinder(IBindingRegistry bindingRegistry)
{
this.bindingRegistry = bindingRegistry;
createScenarioContainerBuilder = new Lazy<Func<ContainerBuilder>>(FindCreateScenarioContainerBuilder, true);
}

public Func<ContainerBuilder> GetCreateScenarioContainerBuilder()
{
var builder = createScenarioContainerBuilder.Value;
if (builder == null)
throw new Exception("Unable to find scenario dependencies! Mark a static method that returns a ContainerBuilder with [ScenarioDependencies]!");
return builder;
}

protected virtual Func<ContainerBuilder> FindCreateScenarioContainerBuilder()
{
var assemblies = bindingRegistry.GetBindingAssemblies();
foreach (var assembly in assemblies)
{
foreach (var type in assembly.GetTypes())
{
foreach (var methodInfo in type.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public).Where(m => Attribute.IsDefined((MemberInfo) m, typeof(ScenarioDependenciesAttribute))))
{
return () => (ContainerBuilder)methodInfo.Invoke(null, null);
}
}
}
return null;

//return (assemblies
// .SelectMany(assembly => assembly.GetTypes(), (_, type) => type)
// .SelectMany(
// type => type.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public).Where(m => Attribute.IsDefined(m, typeof (ScenarioDependenciesAttribute))),
// (_, methodInfo) => (Func<ContainerBuilder>) (() => (ContainerBuilder) methodInfo.Invoke(null, null)))).FirstOrDefault();
}
}
}
10 changes: 10 additions & 0 deletions Plugins/SpecFlow.Autofac.SpecFlowPlugin/IContainerBuilderFinder.cs
@@ -0,0 +1,10 @@
using System;
using Autofac;

namespace SpecFlow.Autofac
{
public interface IContainerBuilderFinder
{
Func<ContainerBuilder> GetCreateScenarioContainerBuilder();
}
}
@@ -0,0 +1,10 @@
using System;
using System.Linq;

namespace SpecFlow.Autofac
{
[AttributeUsage(AttributeTargets.Method)]
public class ScenarioDependenciesAttribute : Attribute
{
}
}
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(SpecFlow_Runtime_TFM)</TargetFrameworks>
<AssemblyOriginatorKeyFile>$(SpecFlow_KeyFile)</AssemblyOriginatorKeyFile>
<SignAssembly>$(SpecFlow_EnableStrongNameSigning)</SignAssembly>
<PublicSign>$(SpecFlow_PublicSign)</PublicSign>

<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<NuspecFile>$(MSBuildThisFileDirectory)SpecFlow.Autofac.nuspec</NuspecFile>

<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta-63127-02" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Autofac" Version="4.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\TechTalk.SpecFlow\TechTalk.SpecFlow.csproj" />
</ItemGroup>

</Project>
32 changes: 32 additions & 0 deletions Plugins/SpecFlow.Autofac.SpecFlowPlugin/SpecFlow.Autofac.nuspec
@@ -0,0 +1,32 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>SpecFlow.Autofac</id>
<version>$version$</version>
<title>SpecFlow Autofac integration plugin</title>
<authors>Gaspar Nagy, Spec Solutions, SpecFlow</authors>
<owners>Gaspar Nagy, Spec Solutions, SpecFlow</owners>
<description>SpecFlow plugin that enables to use Autofac for resolving test dependencies.</description>
<summary>SpecFlow plugin that enables to use Autofac for resolving test dependencies.</summary>
<language>en-US</language>
<projectUrl>http://www.specflow.org</projectUrl>
<iconUrl>http://go.specflow.org/specflow-nuget-icon</iconUrl>
<copyright>Copyright © 2016-2019 Gaspar Nagy, Spec Solutions, SpecFlow</copyright>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="file">LICENSE.txt</license>
<tags>specflow autofac di dependency injection</tags>
<dependencies>
<dependency id="SpecFlow" version="[$version$]" />
<dependency id="Autofac" version="4.0.0" />
</dependencies>
</metadata>

<files>
<file src="build\**\*" target="build" />
<file src="bin\$config$\net45\SpecFlow.Autofac.SpecFlowPlugin.*" target="lib\$SpecFlow_FullFramework_Runtime_TFM$" />
<file src="bin\$config$\netstandard2.0\SpecFlow.Autofac.SpecFlowPlugin.dll" target="lib\$SpecFlow_Core_Runtime_TFM$" />
<file src="bin\$config$\netstandard2.0\SpecFlow.Autofac.SpecFlowPlugin.pdb" target="lib\$SpecFlow_Core_Runtime_TFM$" />

<file src="$SolutionDir$LICENSE.txt" target="LICENSE.txt" />
</files>
</package>
@@ -0,0 +1,11 @@
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" TreatAsLocalProperty="TaskFolder;TaskAssembly">

<ItemGroup>
<None Include="$(_SpecFlow_AutofacPluginPath)" >
<Link>%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>False</Visible>
</None>
</ItemGroup>

</Project>
@@ -0,0 +1,8 @@
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>

<_SpecFlow_AutofacPluginFramework Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' ">netstandard2.0</_SpecFlow_AutofacPluginFramework>
<_SpecFlow_AutofacPluginFramework Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">net45</_SpecFlow_AutofacPluginFramework>
<_SpecFlow_AutofacPluginPath>$(MSBuildThisFileDirectory)\..\lib\$(_SpecFlow_AutofacPluginFramework)\SpecFlow.Autofac.SpecFlowPlugin.dll</_SpecFlow_AutofacPluginPath>
</PropertyGroup>
</Project>
19 changes: 17 additions & 2 deletions TechTalk.SpecFlow.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28714.193
# Visual Studio 15
VisualStudioVersion = 15.0.28307.572
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Setup", "Setup", "{DCE0C3C4-5BC6-4A30-86BE-3FEFF4677A01}"
EndProject
Expand Down Expand Up @@ -88,6 +88,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AzurePipelines", "AzurePipe
build.yml = build.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpecFlow.Autofac.SpecFlowPlugin", "Plugins\SpecFlow.Autofac.SpecFlowPlugin\SpecFlow.Autofac.SpecFlowPlugin.csproj", "{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -350,6 +352,18 @@ Global
{02A08F81-B90F-4EB3-9C30-CE7447DE2012}.Release|Any CPU.Build.0 = Release|Any CPU
{02A08F81-B90F-4EB3-9C30-CE7447DE2012}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU
{02A08F81-B90F-4EB3-9C30-CE7447DE2012}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Release|Any CPU.Build.0 = Release|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.VS2010IntegrationTest|Any CPU.ActiveCfg = Release|Any CPU
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.VS2010IntegrationTest|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -374,6 +388,7 @@ Global
{FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA} = {DCE0C3C4-5BC6-4A30-86BE-3FEFF4677A01}
{02A08F81-B90F-4EB3-9C30-CE7447DE2012} = {A10B5CD6-38EC-4D7E-9D1C-2EBA8017E437}
{48E162BC-9431-450D-8353-66D396DCB5FE} = {577A0375-1436-446C-802B-3C75C8CEF94F}
{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86} = {8BE0FE31-6A52-452E-BE71-B8C64A3ED402}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A4D0636F-0160-4FA5-81A3-9784C7E3B3A4}
Expand Down

0 comments on commit 01d8c62

Please sign in to comment.