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

前端 未结 2 1340
逝去的感伤
逝去的感伤 2021-02-19 22:00

I\'m trying to have the unit tests not rely on calling container.Resolve() for their dependencies.

I\'m currently using AutoFac 2.2.4,

相关标签:
2条回答
  • 2021-02-19 22:03

    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.

    0 讨论(0)
  • 2021-02-19 22:18

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题