我正在尝试让单元测试不依赖于调用 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();
}
}
答案 0 :(得分:11)
最初的问题确实是由于测试框架的设计方式。它们都需要无参数构造函数才能实例化测试实例。理所当然。使用这些框架,不依赖构造函数进行测试初始化。这就是SetUp
方法的目的。总而言之,测试类本身不适合注射。
IMO,当您开发不依赖容器的测试时,这就成了一个问题。毕竟,每个测试类应该关注一个“被测系统”(SUT)。为什么不让setup方法直接实例化该系统并提供每个依赖(通常以伪造的形式)?通过这种方式,您可以有效地从测试中删除另一个不必要的依赖项,即IoC框架。
旁注:我在测试中唯一涉及IoC框架的是“容器测试”。这些测试的重点是验证在使用application or assembly modules初始化容器后,可以从容器中解析某些服务。
答案 1 :(得分:4)
我只是允许我的测试依赖于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);
}
}