为什么这个 Autofac mock 的生命周期是在一个简单的 MSpec 测试中处理的?

Why is this Autofac mock's lifetime disposed in a simple MSpec test?

我有一个基础 class 我正在使用 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 cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.

我猜这与 MSpec 运行测试的方式(通过反射)有关,并且有一段时间没有任何东西主动引用底层生命周期范围内的任何对象导致生命周期被处置的 AutoMock。这是怎么回事,有什么简单的方法可以防止它发生吗?

来自 Autofac.Extras.MoqAutoMock 生命周期范围在 mock 本身被释放时被释放。如果您收到此消息,则意味着 AutoMock 实例已被处置或已失去作用域并且 GC 已将其清除。

鉴于此,有几种可能性。

第一种可能性是您在异步方法调用方面遇到了一些潜在的线程挑战。查看被模拟的方法,我发现您正在验证对 SaveFileAsync 方法的调用。但是,我在那里没有看到任何与异步相关的代码,并且我不完全确定 when/how 测试 运行ning 正在根据当前发布的代码调用它,但是如果存在以下情况异步调用导致测试在一个线程上 运行 而 AutoMock 失去范围或以其他方式在另一个线程上被杀死,我可以看到这种情况发生。

第二种可能性是上下文中静态项和实例项的混合。您将 AutoMock 存储为静态,但它所在的上下文 class 似乎是一个基础 class,旨在提供与实例相关的值。如果两个测试 运行 并行,例如,第一个测试将 AutoMock 设置为它认为需要的值,那么第二个测试将覆盖 AutoMock,第一个将退出作用域,处理作用域。

第三种可能性是在一次测试中多次调用BuildSubject<T>。对 BuildSubject<T> 的调用会初始化 AutoMock。如果您在一个测试中多次调用它,尽管更改了 T 类型,您每次都会踩到 AutoMock 并且相关的生命周期范围将被处理。

第四种可能性是测试排序问题。如果您只是有时看到它而其他时候看不到,则可能是某些测试无意中假定某些设置(例如对 BuildSubject<T> 的调用)已经完成;而其他测试可能不会做出这样的假设,而是会自己调用 BuildSubject<T>。根据测试的顺序 运行,您有时可能很幸运而没有看到异常,但有时您可能 运行 遇到 BuildSubject<T> 在错误的时间被调用的问题并且让你痛苦。