在单元测试中使用 DI 容器 [英] Using DI container in unit tests

查看:30
本文介绍了在单元测试中使用 DI 容器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们一直在使用 Simple Injector 并在相当大的应用中取得了巨大成功.我们一直在为我们所有的生产类使用构造函数注入,并配置 Simple Injector 来填充所有内容,并且一切都很顺利.

We've been using Simple Injector with good success, in a fairly substantial application. We've been using constructor injection for all of our production classes, and configuring Simple Injector to populate everything, and everything's peachy.

不过,我们还没有使用 Simple Injector 来管理单元测试的依赖树.相反,我们一直在手动更新所有内容.

We've not, though, used Simple Injector to manage the dependency trees for our unit tests. Instead, we've been new'ing up everything manually.

我只花了几天时间完成了一次重大重构,几乎所有时间都用于修复单元测试中这些手动构建的依赖树.

I just spent a couple of days working through a major refactoring, and nearly all of my time was in fixing these manually-constructed dependency trees in our unit tests.

这让我想知道 - 有没有人有任何模式来配置他们在单元测试中使用的依赖树?对我们来说,至少,在我们的测试中,我们的依赖树往往相当简单,但有很多.

This has me wondering - does anyone have any patterns they use to configure the dependency trees they use in unit tests? For us, at least, in our tests our dependency trees tend to be fairly simple, but there are a lot of them.

有没有人有管理这些的方法?

Anyone have a method they use to manage these?

推荐答案

对于真正的单元测试(即那些只测试一个类并模拟其所有依赖项的测试),使用 DI 框架没有任何意义.在这些测试中:

For true unit tests (i.e. those which only test one class, and mock all of its dependencies), it doesn't make any sense to use a DI framework. In these tests:

  • 如果您发现有很多重复的代码用于new用您创建的所有模拟创建类的实例,一个有用的策略是创建所有模拟并在您的 Setup 方法中为被测主题创建实例(这些都可以是私有实例字段),然后每个单独测试的排列"区域只需调用相应的 Setup() 代码关于它需要模拟的方法.这样,您最终每个测试类只有一个 new PersonController(...) 语句.
  • 如果您需要创建大量域/数据对象,那么创建以用于测试的合理值开始的 Builder 对象会很有用.因此,与其在你的代码中调用一个巨大的构造函数,并使用一堆假值,你大多只是调用,例如,var person = new PersonBuilder().Build(),可能只是一些链式方法调用您在该测试中特别关心的数据片段.您可能也对 AutoFixture 感兴趣,但我从未使用过它,所以我无法保证.
  • if you find that you have a lot of repetitive code for newing up an instance of your class with all the mocks you've created, one useful strategy is to create all of your mocks and create the instance for the subject-under-test in your Setup method (these can all be private instance fields), and then each individual test's "arrange" area just has to call the appropriate Setup() code on the methods it needs to mock. This way, you end up with only one new PersonController(...) statement per test class.
  • if you're needing to create a lot of domain/data objects, it's useful to create Builder objects that start with sane values for testing. So instead of invoking a huge constructor all over your code, with a bunch of fake values, you're mostly just calling, e.g., var person = new PersonBuilder().Build(), possibly with just a few chained method calls for pieces of data that you specifically care about in that test. You may also be interested in AutoFixture, but I've never used it so I can't vouch for it.

如果您正在编写集成测试,您需要在其中测试系统多个部分之间的交互,但您仍然需要能够模拟特定部分,请考虑为您的系统创建 Builder 类服务,所以你可以说,例如var personController = new PersonControllerBuilder.WithRealDatabase(connection).WithAuthorization(new AllowAllAuthorizationService()).Build().

If you're writing integration tests, where you need to test the interaction between several parts of the system, but you still need to be able to mock specific pieces, consider creating Builder classes for your services, so you can say, e.g. var personController = new PersonControllerBuilder.WithRealDatabase(connection).WithAuthorization(new AllowAllAuthorizationService()).Build().

如果您正在编写端到端或场景"测试,您需要在其中测试整个系统,那么设置您的 DI 框架是有意义的,利用与您的实际产品使用的配置代码相同的配置代码.您可以稍微更改配置,以便更好地以编程方式控制用户登录等内容.您仍然可以利用您创建的其他构建器类来构建数据.

If you're writing end-to-end, or "scenario" tests, where you need to test the whole system, then it makes sense to set up your DI framework, leveraging the same configuration code that your real product uses. You can alter the configuration slightly to give yourself better programmatic control over things like which user is logged in and such. You can still leverage the other builder classes you've created for constructing data, too.

var user = new PersonBuilder().Build();
using(Login.As(user))
{
     var controller = Container.Get<PersonController>();
     var result = controller.GetCurrentUser();
     Assert.AreEqual(result.Username, user.Username)
}

这篇关于在单元测试中使用 DI 容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆