A .NET Unit Test without a parameterless constructor, to facilitate dependency injection

一曲冷凌霜 提交于 2019-12-04 00:52:14

The initial problem is indeed due to how the testing frameworks are designed. They all require a parameterless constructor in order to instantiate test instances. And rightfully so. With these frameworks, the constructor is not to be relied on for test initialization. That is the purpose of the SetUp method. All in all, the test classes themselves are not suited for injection.

And IMO, this becomes a non-issue when you develop your tests to not depend on the container. After all, each test class should focus on one "system under test" (SUT). Why not have the setup method instantiate that system directly and provide each dependency (usually in the form of fakes)? By doing it this way you have effectively removed another unnecessary dependency from your tests, namely the IoC framework.

On a side note: the only time I involve the IoC framework in my tests is in my "container tests". These tests focus on verifying that certain services can be resolved from the container after the container have been initialized with application or assembly modules.

Jim Bolla

I just allow my tests to have a dependency on Autofac, although I encapsulate it. All of my TestFixtures inherit from Fixture, which is defined as such:

public class Fixture
{
    private static readonly IContainer MainContainer = Ioc.Build();
    private readonly TestLifetime _testLifetime = new TestLifetime(MainContainer);

    [SetUp]
    public void SetUp()
    {
        _testLifetime.SetUp();
    }

    [TearDown]
    public void TearDown()
    {
        _testLifetime.TearDown();
    }

    protected TService Resolve<TService>()
    {
        return _testLifetime.Resolve<TService>();
    }

    protected void Override(Action<ContainerBuilder> configurationAction)
    {
        _testLifetime.Override(configurationAction);
    }
}

public class TestLifetime
{
    private readonly IContainer _mainContainer;

    private bool _canOverride;
    private ILifetimeScope _testScope;

    public TestLifetime(IContainer mainContainer)
    {
        _mainContainer = mainContainer;
    }

    public void SetUp()
    {
        _testScope = _mainContainer.BeginLifetimeScope();
        _canOverride = true;
    }

    public void TearDown()
    {
        _testScope.Dispose();
        _testScope = null;
    }

    public TService Resolve<TService>()
    {
        _canOverride = false;
        return _testScope.Resolve<TService>();
    }

    public void Override(Action<ContainerBuilder> configurationAction)
    {
        _testScope.Dispose();

        if (!_canOverride)
            throw new InvalidOperationException("Override can only be called once per test and must be before any calls to Resolve.");

        _canOverride = false;
        _testScope = _mainContainer.BeginLifetimeScope(configurationAction);
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!