我试图让单元测试不依赖于调用 container.Resolve <T>() 对于他们的依赖。
我正在使用 AutoFac 2.2.4,并尝试过 xUnit.NET 和 NUnit的,但两者都有 这个问题:
没有为此对象定义无参数构造函数
我如何解决这个问题?它是一个特定的单元测试框架,它将支持这个,或者只是如何配置所述框架?
我不应该这样做吗?或者我可以设置测试类来使用只有依赖项的构造函数吗?
这是一些代码:
public class ProductTests : BaseTest
{
readonly private IProductRepository _repo;
public ProductTests(IProductRepository r)
{
_repo = r;
}
//working unit tests here with default constructor
}
我是否选择在基类构造函数中错误地初始化容器?
public abstract class BaseTest
{
protected BaseTest()
{
var builder = new ContainerBuilder();
builder.RegisterType<ProductRepository>().As<IProductRepository>();
builder.Build();
}
}
最初的问题确实是由于测试框架的设计方式。它们都需要无参数构造函数才能实例化测试实例。理所当然。使用这些框架,不依赖构造函数进行测试初始化。这就是目的 SetUp
方法。总而言之,测试类本身不适合注射。
而IMO,当您开发不依赖容器的测试时,这就成了一个问题。毕竟,每个测试类应该关注一个“被测系统”(SUT)。为什么不让setup方法直接实例化该系统并提供每个依赖(通常以假货的形式)?通过这种方式,您可以有效地从测试中删除另一个不必要的依赖项,即IoC框架。
旁注:我在测试中唯一涉及IoC框架的是“容器测试”。这些测试的重点是验证在初始化容器后可以从容器中解析某些服务 应用程序或组装模块。
我只是允许我的测试依赖于Autofac,尽管我将其封装起来。我的所有TestFixtures都继承自Fixture,其定义如下:
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);
}
}