Search code examples
autofacmspecautomocking

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


I've got a base class I'm using with MSpec which provides convenience methods around 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>();
    }
}

Occasionally, I'm seeing an exception happen when attempting to retrieve a Mock like so:

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

Here's the exception:

System.ObjectDisposedExceptionInstances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.

I'm guessing it has something to do with the way MSpec runs the tests (via reflection) and that there's a period of time when nothing actively has references to any of the objects in the underlying lifetime scope being used by AutoMock which causes the lifetimescope to get disposed. What's going on here, and is there some simple way for me to keep it from happening?


Solution

  • The AutoMock lifetime scope from Autofac.Extras.Moq is disposed when the mock itself is disposed. If you're getting this, it means the AutoMock instance has been disposed or has otherwise lost scope and the GC has cleaned it up.

    Given that, there are a few possibilities.

    The first possibility is that you've got some potential threading challenges around async method calls. Looking at the method that's being mocked, I see you're verifying the call to a SaveFileAsync method. However, I don't see any async related code in there, and I'm not entirely sure when/how the tests running are calling it given the currently posted code, but if there is a situation where an async call causes the test to run on one thread while the AutoMock loses scope or otherwise gets killed on the other thread, I could see this happening.

    The second possibility is the mix of static and instance items in the context. You are storing the AutoMock as a static, but it appears the context class in which it resides is a base class that is intended to supply instance-related values. If two tests run in parallel, for example, the first test will set the AutoMock to what it thinks it needs, then the second test will overwrite the AutoMock and the first will go out of scope, disposing the scope.

    The third possibility is multiple calls to BuildSubject<T> in one test. The call to BuildSubject<T> initializes the AutoMock. If you call that multiple times in one test, despite changing the T type, you'll be stomping the AutoMock each time and the associated lifetime scope will be disposed.

    The fourth possibility is a test ordering problem. If you only see it sometimes but not other times, it could be that certain tests inadvertently assume that some setup, like the call to BuildSubject<T>, has already been done; while other tests may not make that assumption and will call BuildSubject<T> themselves. Depending on the order the tests run, you may sometimes get lucky and not see the exception, but other times you may run into the problem where BuildSubject<T> gets called at just the wrong time and causes you pain.