在大型依赖对象图上注入Mocks

时间:2011-11-02 15:52:39

标签: c# unit-testing dependency-injection moq autofixture

对于我想要测试的对象,我有一个相当重要的依赖图。无需在任何地方注册模拟,解决依赖关系的最简单方法是什么?

例如,我有一个像这样的依赖图:

  PublicApi
    ApiService
      AccountingFacade
         BillingService
           BillingValidation
           BillingRepository
         UserService
           UserRepository

我想测试PublicApi.CreateUser(),我想让它运行所有代码,但我想模拟存储库,所以我不必向数据库写任何东西。我应该只使用DI容器并注册我的所有服务,用mocks替换存储库,然后解析PublicApi并运行方法吗?

我正在调查AutoFixture,看起来它可能能够处理这样的事情,但我无法完全围绕整个'Freeze'与'Register'并且它与Moq集成。

2 个答案:

答案 0 :(得分:7)

对于Unittests,您应该只模拟直接依赖项。在您的情况下,您创建PublicApi并为ApiService注入模拟,并验证PublicApi是否使用ApiService模拟中的正确值调用相应的方法。

与测试与更深层次的依赖项隔离的所有其他组件的方法相同。

如果要测试多个组件的组合,那不是单元测试,而是集成测试。因此,这取决于你如何将你的课程放在一起。例如如果您使用的是IoC容器,它可能支持以某种方式替换存储库的配置。在这种情况下,您可以使用应用程序的配置并使用模拟替换存储库,也可能替换视图。

答案 1 :(得分:1)

至少这可能没什么用,但无论如何我都会说。

您似乎试图一次性测试太多,为什么不测试BillingService - > BillingValidation,然后是BillingService - > BillingRepository等。这样你就可以得到一套测试证明每一个都有效,然后当你在PublicApi Layer上时你只需要模拟ApiService,因为你已经测试了它下面的所有内容,所以测试中没有任何价值再一次。

一般来说,我一次只测试1层,但我不知道你的完整场景,所以你可能有一些我不会考虑的事情,所以如果是这样的话,你真的需要一起测试所有这一切我只会引入像Ninject之类的简单轻量级的DI框架。

这样你就可以将所有类型绑定到模拟中,然后从中实现PublicApi。

使用ninject,它看起来像:

Kernel.Bind<UserRepository>.ToConst(YourMockUserRepositoryInstance);
Kernel.Bind<UserService>.ToConst(YourMockUserServiceInstance);
Kernel.Bind<BillingRepository>.ToConst(YourMockBillingRepositoryInstance);
Kernel.Bind<BillingValidation>.ToConst(YourMockBillingValidationInstance);
Kernel.Bind<BillingService>.ToConst(YourMockBillingServiceInstance);
Kernel.Bind<AccountingFacade>.ToConst(YourMockAccountingFacadeInstance);
Kernel.Bind<ApiService>.ToConst(YourMockApiServiceInstance);
Kernel.Bind<PublicApi>.ToSelf();

var publicApi = Kernel.Get<PublicApi>();

虽然你必须问问自己,你在这里测试什么?如果它只是我在第一次提到时会做的互动,如果它更多可能会考虑后者的选择。无论哪种方式,我希望它给你一些选择。