Почему время жизни этого макета Autofac указано в простом тесте MSpec?

У меня есть базовый класс, который я использую с MSpec, который предоставляет удобные методы вокруг AutoMock:

public abstract class SubjectBuilderContext
{
    static AutoMock _container;

    protected static ISubjectBuilderConfigurationContext<T> BuildSubject<T>()
    {
        _container = AutoMock.GetLoose();
        return new SubjectBuilderConfigurationContext<T>(_container);
    }

    protected static Mock<TDouble> GetMock<TDouble>() 
        where TDouble : class
    {
        return _container.Mock<TDouble>();
    }
}

Иногда я вижу исключение при попытке получить Mock следующим образом:

It should_store_the_receipt = () => GetMock<IFileService>().Verify(f => f.SaveFileAsync(Moq.It.IsAny<byte[]>(), Moq.It.IsAny<string>()), Times.Once());

Вот исключение:

System.ObjectDisposedExceptionInstances не могут быть разрешены, а вложенные времена жизни не могут быть созданы из этого LifetimeScope, так как он уже удален.

Я предполагаю, что это как-то связано с тем, как MSpec запускает тесты (посредством отражения), и что есть период времени, когда ничто активно не имеет ссылок на какие-либо объекты в базовой области действия, используемой AutoMock, что вызывает жизненный цикл утилизироваться. Что здесь происходит, и есть ли простой способ предотвратить это?


person Derek Greer    schedule 21.07.2015    source источник
comment
По мне так он идеально подходит для MFakes. Он должен абстрагироваться от фиктивных фреймворков и их сложностей. Есть ли причина, по которой вы не используете MFakes?   -  person Simon Hohenadl    schedule 21.07.2015
comment
Спасибо за рекомендацию. Я могу более внимательно взглянуть на MFakes, но я хотел бы сначала понять, что происходит с моим использованием AutoMocks, в надежде не совершать ту же ошибку в другом контексте позже.   -  person Derek Greer    schedule 22.07.2015


Ответы (1)


Область действия AutoMock из Autofac.Extras.Moq удаляется при удалении самого макета. Если вы получаете это, это означает, что экземпляр AutoMock был удален или иным образом потерял область действия, и GC очистил его.

Учитывая это, есть несколько возможностей.

Первая возможность заключается в том, что у вас возникли потенциальные проблемы с потоками вокруг вызовов асинхронных методов. Глядя на имитируемый метод, я вижу, что вы проверяете вызов метода SaveFileAsync. Тем не менее, я не вижу там никакого кода, связанного с асинхронностью, и я не совсем уверен, когда/как запущенные тесты вызывают его с учетом текущего опубликованного кода, но если есть ситуация, когда асинхронный вызов заставляет тест работать в одном потоке, в то время как AutoMock теряет область действия или иным образом уничтожается в другом потоке, я мог видеть, как это происходит.

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

Третий вариант — несколько вызовов BuildSubject<T> в одном тесте. Вызов BuildSubject<T> инициализирует AutoMock. Если вы вызовете это несколько раз в одном тесте, несмотря на изменение типа T, вы будете топать AutoMock каждый раз, и связанная область времени жизни будет удалена.

Четвертая возможность — проблема с заказом тестов. Если вы видите это только иногда, но не в другое время, возможно, некоторые тесты непреднамеренно предполагают, что некоторые настройки, такие как вызов BuildSubject<T>, уже были выполнены; в то время как другие тесты могут не делать этого предположения и сами вызывать BuildSubject<T>. В зависимости от порядка выполнения тестов иногда вам может повезти и вы не увидите исключение, но в других случаях вы можете столкнуться с проблемой, когда BuildSubject<T> вызывается в неподходящее время и причиняет вам боль.

person Travis Illig    schedule 21.07.2015
comment
Спасибо за ответ, Трэвис. Я считаю, что теперь я могу исключить первую теорию, касающуюся сценария, имеющего какое-либо отношение к асинхронности (по крайней мере, как единственную причину), поскольку теперь я вижу это и с неасинхронными методами. Я также думаю, что могу исключить вторую теорию, основанную на этом ответе Александра Гросса, который указывает, что тесты MSpec выполняются последовательно: stackoverflow.com/questions/14865859/. Я могу подтвердить, что третья и четвертая теории неверны (множественные или отсутствующие вызовы BuildSubject). - person Derek Greer; 22.07.2015
comment
Что ж, где-то по ходу дела AutoMock утилизируется. Без ваших реальных тестов и репродукции все, что я могу сделать, это догадаться, как это сделать. - person Travis Illig; 22.07.2015
comment
Я обнаружил, что контейнер утилизируется :/ - person Derek Greer; 22.07.2015