Использование областей применения SimpleInjector в хуках жизненного цикла SpecFlow 3 - PullRequest
0 голосов
/ 20 декабря 2018

Я пытаюсь настроить свои тесты Specflow (V. 3.0.155 beta) в .Net Core 2.1 Web API, и я придерживаюсь структуры, которую я использовал в некоторых предыдущих проектах .Net Framework Web API.Мы работали над этим.

Но у меня возникают проблемы, когда я пытаюсь разрешить свои зависимости, кажется, что я выхожу из области видимости, поэтому, когда я пытаюсь разрешить что-то, то есть мой тестВ контексте данных я получаю следующую ошибку от SpecFlow:

Сообщение: SimpleInjector.ActivationException: зарегистрированный делегат для типа ProjectNexusContext выдал исключение.ProjectNexusContext зарегистрирован как стиль жизни «Async Scoped», но экземпляр запрашивается вне контекста активной области (Async Scoped).----> SimpleInjector.ActivationException: ProjectNexusContext зарегистрирован как образ жизни с асинхронной областью, но экземпляр запрашивается вне контекста активной области (асинхронной области).

Трассировка стека:

Результат StackTrace: в SimpleInjector.InstanceProducer.GetInstance ()
в SimpleInjector.Container.GetInstanceTService в ProjectNexus.ApplicationContext.ResolveT в C: \ Users \ dawes \ source \ repos \ Projectexexus ProjectApplicationContext.cs: строка 18 в ProjectNexus.Engine.Specs.LifecycleTestHooks.AfterStep () в C: \ Users \ dawes \ source \ repos \ ProjectNexus \ ProjectNexus.Engine.Specs \ LifecycleTestHooks.cs: строка 37 в TechTalk.SpecFlow.Bindings.BindingInvoker.InvokeBinding (IBinding привязка, IContextManager contextManager, аргументы Object [], ITestTracer testTracer, TimeSpan & duration) в D: \ a \ 1 \ s \ TechTalk.SpecFlow \ Bindings \ BindingInvoker.cs: строка 73 в TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.InvokeHook (IBindingInvoker invoker, IHookBinding hookBinding, HookType hookType) в D: \ a \ 1 \ s \ TechTalk.SpecFlow \ Infrastructure \ TestExecutionEngine.cs: строка 246 в TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.FireEvents (HookType hookType) в D: \ as \ TechTalk.SpecFlow \ Infrastructure \ TestExecutionEngine.cs: строка 232 в TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStep (IContextManager contextManager, stepInstance stepInstance) в D: \ a \ 1 \ s \ TechTalkstructure.Sec. Test.SecF: строка 367 в TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.Step (StepDefinitionKeyword stepDefinitionKeyword, строковое ключевое слово, текст строки, строковый multilineTextArg, таблица tableArg) в D: \ a \ 1 \ s \ TechTalk.SpecFlow \ Infrastructure \.475 в TechTalk.SpecFlow.TestRunner.Given (текст строки, строковый multilineTextArg, таблица 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 In.feature: строка 7 в ProjectNexus.Engine.Specs.Tests.Client.Authentication.LogInFeature.LogInWithAValidUsernameAndPassword () в C: Users \ Users\ source \ repos \ ProjectNexus \ ProjectNexus.Engine.Specs \ Tests \ Client \ Authentication \ Log In.feature: строка 6 - АктивацияException в SimpleInjector.Scope.GetScopelessInstance [TImplementation] (ScopedRegistration 1 registration) at SimpleInjector.Advanced.Internal.LazyScopedRegistration 1.GetInstance (область действия области)at lambda_method (Closure) at SimpleInjector.InstanceProducer.GetInstance () Сообщение о результате: SimpleInjector.ActivationException: зарегистрированный делегат для типа ProjectNexusContext вызвал исключение.ProjectNexusContext зарегистрирован как стиль жизни «Async Scoped», но экземпляр запрашивается вне контекста активной области (Async Scoped).----> SimpleInjector.ActivationException: ProjectNexusContext зарегистрирован как образ жизни с асинхронной областью действия, но экземпляр запрашивается вне контекста активной области действия (асинхронной области действия).РезультатStandardOutput: данные Следующие пользователи хранятся в базе данных --- аргумент шага таблицы --- |Forneame |Фамилия |Имя пользователя|Джон |Смит |JS001 |-> ошибка: зарегистрированный делегат для типа ProjectNexusContext выдал исключение.ProjectNexusContext зарегистрирован как образ жизни «Async Scoped», но экземпляр запрашивается вне контекста активной области (Async Scoped).

Я использую Simple Injector 4.4.2 и в моем тестепроект, и я настраиваю свой контейнер и регистрирую свои экземпляры в методе статического класса, который вызывается в хуке жизненного цикла BeforeTestRun SpecFlow.

Затем в хуке BeforeScenario я начинаю новую область действия AsyncScopedLifestyle в указанном контейнере, котораябыть использованным повсюду для данного сценария.Далее, в моем хакере AfterScenario я избавляюсь от этого образа жизни.Я просмотрел документацию SimpleInjector, касающуюся этого образа жизни, и знаю, что это исключение выдается, когда он находится вне контекста активной области, но я не могу понять, почему я нахожусь вне активного контекста!

После проверки области действияв хуке BeforeStep я вижу, что свойство CurrentScope в ScopeManager, хотя и не удалено, имеет значение null, поэтому я явно вне контекста активной области.

Это никогда ранее не было проблемой, и кодточно так же, как в вышеупомянутых предыдущих проектах, которые используют SimpleInjector в своих тестовых проектах точно таким же образом.Я даже прошел их и проверил область видимости в крючке BeforeStep, а CurrentScope в ScopeManager был областью действия, поэтому они явно не выходят за пределы этой области.

Я надеялся, что кто-то может увидеть что-то, что я 'отсутствует или есть некоторые предложения относительно того, как решить эту проблему.

Я включил код для моих перехватчиков 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>();
    }
}

1 Ответ

0 голосов
/ 02 января 2019

Перед каждой спецификацией вам нужно будет создать новый Контейнер для использования, так как я не думаю, что повторное использование сделает то, что вы ожидаете с текущей настройкой.

Вы можете добавить метод с именем SetContainer в вашем ApplicationContext примерно так:

public static void SetContainer(Container container)
{
    Container = container;
}

И затем в вашем BeforeScenario хуке вы можете запустить свою конфигурацию IoC, но создать новый контейнер и использовать свой SetContainer метод

ОБНОВЛЕНИЕ : Похоже, что с Specflow 3-beta активная область видимости удаляется, как только она покидает метод тестового хука (например, BeforeScenario)

На данный момент вы можете зарегистрировать свои экземпляры сединый образ жизни, и используйте новый контейнер для каждого теста (как вы можете сделать с предложением, которое я сделал выше).

Я попробовал идентичный код в Specflow 2.3.2, и он отлично работает, но ломается в Specflow 3-бета.

...