Jef*_*ege 9 c# unit-testing dependency-injection ioc-container simple-injector
我们一直在使用Simple Injector取得了很好的成功,在一个相当实际的应用程序中.我们一直在为所有生产类使用构造函数注入,并配置Simple Injector来填充所有内容,而且一切都很好.
但是,我们没有使用Simple Injector来管理单元测试的依赖树.相反,我们一直在手动创新.
我花了几天时间完成了一次重大的重构,几乎所有的时间都是在我们的单元测试中修复这些手动构造的依赖树.
这让我感到疑惑 - 是否有人使用任何模式来配置他们在单元测试中使用的依赖树?对我们来说,至少在我们的测试中,我们的依赖树往往相当简单,但有很多.
任何人都有他们用来管理这些的方法吗?
Str*_*ior 14
对于真正的单元测试(即那些只测试一个类,并模拟其所有依赖项的测试),使用DI框架没有任何意义.在这些测试中:
new创建你所创建的所有模拟的类的实例,一个有用的策略是创建所有的模拟并为测试中的主题创建实例你的安装方法(这些都可以是私有实例字段),然后每个单独测试的"安排"区域只需调用Setup()模拟所需方法的相应代码.这样,new PersonController(...)每个测试类最终只有一个语句.var person = new PersonBuilder().Build(),可能只需要对该测试中特别关注的数据片段进行几次链式方法调用.您可能也对AutoFixture感兴趣,但我从未使用它,所以我不能保证它.如果您正在编写集成测试,您需要测试系统的多个部分之间的交互,但仍需要能够模拟特定部分,请考虑为您的服务创建Builder类,因此您可以说,例如var personController = new PersonControllerBuilder.WithRealDatabase(connection).WithAuthorization(new AllowAllAuthorizationService()).Build().
如果您正在编写端到端或"场景"测试,您需要测试整个系统,那么设置DI框架是有意义的,利用您的实际产品使用的相同配置代码.您可以稍微更改配置,以便更好地控制用户登录等事项.您仍然可以利用您为构建数据而创建的其他构建器类.
var user = new PersonBuilder().Build();
using(Login.As(user))
{
var controller = Container.Get<PersonController>();
var result = controller.GetCurrentUser();
Assert.AreEqual(result.Username, user.Username)
}
Run Code Online (Sandbox Code Playgroud)
Ste*_*ven 10
请勿在单元测试中使用DI容器.在单元测试中,您尝试单独测试一个类或模块,并且该区域中的DI容器几乎没有用处.
集成测试的情况有所不同,因为您要测试系统中的组件如何集成和协同工作.在这种情况下,您经常使用您的生产DI配置并将您的一些服务换成假服务(例如您的MailService),但尽可能贴近真实的服务.在这种情况下,您使用容器来解析整个对象图.
在单元测试中使用DI容器的愿望通常源于无效模式.例如,如果您尝试在每个测试中创建具有所有依赖项的测试中的类,您将获得大量重复的初始化代码,并且在您的测试类中稍有变化就可以在系统中波动并要求您改变了几十个单元测试.这显然会导致可维护性问题.
在过去帮助我很多的一个模式是使用一个简单的测试SUT特定的工厂方法.此方法集中创建要测试的类,并最小化在测试类的依赖项发生更改时需要进行的更改量.这就是这种工厂方法的样子:
private ClassUnderTest CreateClassUnderTest(
ILogger logger = null, IMailSender mailSender = null,
IEventPublisher publisher = null)
{
return new ClassUnderTest(
logger ?? new FakeLogger(),
mailSender ?? new FakeMailer(),
publisher ?? new FakePublisher());
}
Run Code Online (Sandbox Code Playgroud)
此工厂方法参数复制了类的构造函数参数,但使它们都是可选的.对于调用者未提供的任何特定依赖项,将注入新的伪实现.
这非常有效,因为在大多数测试中,您只对一两个依赖项感兴趣.该类可能需要其他依赖项才能运行,但对于该特定测试并不感兴趣.因此,工厂方法允许您仅提供对手头测试感兴趣的依赖项,同时消除未使用的依赖项的噪声.因此工厂方法允许您编写以下测试:
public void Test() {
// Arrange
var logger = new ListLogger();
ClassUnderTest sut = CreateClassUnderTest(logger: logger);
// Act
sut.DoSomething();
// Arrange
Assert.IsTrue(logger.Count > 0);
}
Run Code Online (Sandbox Code Playgroud)
如果您有兴趣学习如何编写可读,可信和可维护(RTM)测试,我建议您阅读Roy Osherove的书"单元测试的艺术"(第二版).这对我编写出色的单元测试有很大的帮助.如果您有兴趣了解有关依赖注入的更多信息,那么我的书是最明显的书.