我正在尝试在.Net Core 2.1 Web API中设置我的Specflow(V。3.0.155 beta)测试,并且我遵循的是在以前的某些.Net Framework Web API项目中使用的结构。我已经努力做到了。
但是,当我尝试解决依赖关系时遇到麻烦,似乎我已经超出范围,因此当我尝试解决某些问题(即测试数据上下文)时,我从SpecFlow中收到以下错误:
消息:SimpleInjector.ActivationException:注册的委托 用于类型 ProjectNexusContext引发了异常。 ProjectNexusContext是 已注册为“异步作用域”生活方式,但需要实例 在有效(异步作用域)范围的上下文之外。 ----> SimpleInjector.ActivationException:ProjectNexusContext是 已注册为“异步作用域”生活方式,但需要实例 超出有效(异步作用域)作用域的上下文。
堆栈跟踪:
结果StackTrace:位于SimpleInjector.InstanceProducer.GetInstance()
在SimpleInjector.Container.GetInstanceTService在 中的ProjectNexus.ApplicationContext.ResolveT C:\ Users \ dawes \ source \ repos \ ProjectNexus \ ProjectNexus \ ApplicationContext.cs:line 18在ProjectNexus.Engine.Specs.LifecycleTestHooks.AfterStep()中 C:\ Users \ dawes \ source \ repos \ ProjectNexus \ ProjectNexus.Engine.Specs \ LifecycleTestHooks.cs:line 37岁 TechTalk.SpecFlow.Bindings.BindingInvoker.InvokeBinding(IBinding 绑定,IContextManager contextManager,Object []参数, ITestTracer testTracer,TimeSpan和持续时间) D:\ a \ 1 \ s \ TechTalk.SpecFlow \ Bindings \ BindingInvoker.cs:第73行位于 TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.InvokeHook(IBindingInvoker 调用程序,IHookBinding hookBinding,HookType hookType) D:\ a \ 1 \ s \ TechTalk.SpecFlow \ Infrastructure \ TestExecutionEngine.cs:line 246点 TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.FireEvents(HookType hookType)中 D:\ a \ 1 \ s \ TechTalk.SpecFlow \ Infrastructure \ TestExecutionEngine.cs:line 232点 TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStep(IContextManager contextManager,StepInstance stepInstance) D:\ a \ 1 \ s \ TechTalk.SpecFlow \ Infrastructure \ TestExecutionEngine.cs:line 367点 TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.Step(StepDefinitionKeyword stepDefinitionKeyword,String关键字,String文本,String multilineTextArg,Table tableArg)在 D:\ a \ 1 \ s \ TechTalk.SpecFlow \ Infrastructure \ TestExecutionEngine.cs:line 475在TechTalk.SpecFlow.TestRunner.Given(String text,String multilineTextArg,Table tableArg,String关键字) D:\ a \ 1 \ s \ TechTalk.SpecFlow \ TestRunner.cs:第75行位于 ProjectNexus.Engine.Specs.Tests.Client.Authentication.LogInFeature.FeatureBackground() 在 C:\ Users \ dawes \ source \ repos \ ProjectNexus \ ProjectNexus.Engine.Specs \ Tests \ Client \ Authentication \ Log 功能:第7行 ProjectNexus.Engine.Specs.Tests.Client.Authentication.LogInFeature.LogInWithAValidUsernameAndPassword() 在 C:\ Users \ dawes \ source \ repos \ ProjectNexus \ ProjectNexus.Engine.Specs \ Tests \ Client \ Authentication \ Log 功能:第6行 --SimpleInjector.Scope.GetScopelessInstance [TImplementation](ScopedRegistration1 registration) at SimpleInjector.Advanced.Internal.LazyScopedRegistration
1.GetInstance(Scope 范围)在lambda_method(Closure)在 SimpleInjector.InstanceProducer.GetInstance()结果消息: SimpleInjector.ActivationException:类型的注册委托 ProjectNexusContext引发了异常。 ProjectNexusContext是 已注册为“异步作用域”生活方式,但需要实例 在有效(异步作用域)范围的上下文之外。 ----> SimpleInjector.ActivationException:ProjectNexusContext是 已注册为“异步作用域”生活方式,但需要实例 在有效(异步作用域)范围的上下文之外。结果 StandardOutput:给定以下用户存储在数据库中 ---表步参数--- |前言|姓|用户名| |约翰|史密斯| js001 | ->错误:ProjectNexusContext类型的注册委托引发了异常。 ProjectNexusContext已注册为“异步作用域” 生活方式,但实例是在 活动(异步作用域)作用域。
我在测试项目中使用Simple Injector 4.4.2,并设置了容器 并在静态类方法中注册我的实例,该方法在BeforeTestRun SpecFlow生命周期挂钩中被调用。
然后在我的BeforeScenario挂钩中,在所述容器上开始一个新的AsyncScopedLifestyle范围,该范围将在给定场景中使用。接下来,我在AfterScenario挂钩中处理这种生活方式。我查看了有关这种生活方式的SimpleInjector文档,并且知道在活动范围的上下文之外时会抛出此异常,但是我看不到为什么在活动范围之外!
在检查BeforeStep挂钩中的Scope之后,我可以看到,尽管没有处理,但ScopeManager中的CurrentScope属性为null,因此我显然不在活动的范围上下文中。
这以前从来都不是问题,并且代码与上述先前的项目完全相同,后者以完全相同的方式在其测试项目中使用SimpleInjector。我什至逐步检查它们,并检查BeforeStep挂钩中的范围,并且ScopeManager中的CurrentScope是范围,因此此时它们显然不在范围之外。
我希望有人可能看到我缺少的东西,或者对如何解决这个问题有一些建议。
我在下面包括了SpecFlow挂钩和IoC设置的代码:
SpecFlow挂钩:
[Binding]
public class LifecycleTestHooks
{
[BeforeTestRun]
public static void BeforeTestRun()
{
TestIocConfiguration.Configure();
}
[BeforeScenario]
public void BeforeScenario()
{
TestIocConfiguration.StartExecutionScope();
}
[BeforeStep]
public void BeforeStep()
{
var projectDbContext = ApplicationContext.Resolve<ProjectNexusContext>();
projectDbContext.Database.BeginTransaction();
}
[AfterStep]
public void AfterStep()
{
var projectDbContext = ApplicationContext.Resolve<ProjectNexusContext>();
if (projectDbContext.Database.CurrentTransaction != null)
{
try
{
projectDbContext.Database.CommitTransaction();
}
catch (Exception)
{
projectDbContext.Database.RollbackTransaction();
throw;
}
}
TestIocConfiguration.CheckExecutionScope();
}
[AfterScenario]
public void AfterScenario()
{
TestIocConfiguration.EndExecutionScope();
}
}
IoC设置
public class TestIocConfiguration
{
static Container container;
public static void Configure()
{
container = ApplicationContext.Container;
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
EngineInitialisation.Initialise();
container.Options.AllowOverridingRegistrations = true;
RegisterTestDatabaseContext();
RegisterTestApplicationConfiguration();
RegisterTestLdapConnectionService();
RegisterTestContext();
}
private static void RegisterTestDatabaseContext()
{
var testContext =
new ProjectNexusContext(new TestContextHelper().GetDbContextOptionsBuilder());
testContext.Database.OpenConnection();
testContext.Database.EnsureCreated();
container.Register<ProjectNexusContext>(() => testContext, Lifestyle.Scoped);
}
private static void RegisterTestLdapConnectionService()
{
container.Register<ILdapConnectionService, TestLdapConnectionService>(Lifestyle.Scoped);
}
private static void RegisterTestApplicationConfiguration()
{
var appConfig = new ApplicationConfiguration
{
LdapHost = "",
LdapPort = 0,
ApplicationSecret = "TestSecret",
TokenExpirationDays = 1
};
container.Register<ApplicationConfiguration>(() => appConfig, Lifestyle.Scoped);
}
private static void RegisterTestContext()
{
container.Register<TestContext>(Lifestyle.Singleton);
}
public static Scope StartExecutionScope()
{
return AsyncScopedLifestyle.BeginScope(container);
}
public static void EndExecutionScope()
{
Lifestyle.Scoped.GetCurrentScope(container)?.Dispose();
}
}
应用上下文
public class ApplicationContext
{
public static readonly Container Container;
public static readonly Mapper Mapper;
static ApplicationContext()
{
Container = new Container();
}
public static T Resolve<T>() where T : class
{
return Container.GetInstance<T>();
}
public static Cast Resolve<T, Cast>()
where Cast : T
where T : class
{
return (Cast)Container.GetInstance<T>();
}
}
答案 0 :(得分:1)
在每个规格之前,您需要创建一个新的容器才能使用,因为我认为重复使用不会满足您当前设置的期望。
您可以在SetContainer
中添加一个名为ApplicationContext
的方法,如下所示:
public static void SetContainer(Container container)
{
Container = container;
}
然后在您的BeforeScenario
钩中,您可以运行IoC配置,但是要新建一个容器并使用SetContainer
方法
更新:似乎在Specflow 3-beta中,活动范围一离开测试挂钩方法(例如,BeforeScenario)就立即被处置
现在,您可以使用单例生活方式注册实例,并为每个测试使用新的容器(就像您可以按照我上面的建议做的那样)。
我在Specflow 2.3.2中尝试了相同的代码,但工作正常,但在Specflow 3-beta中中断了。