如何在单元测试中正确使用 IoC

本文关键字:IoC 单元测试 | 更新日期: 2023-09-27 18:34:01

我们有一个WPF应用程序。许多 ViewModel 使用与必须模拟的依赖项相同的依赖项。有时,ViewModel 的构造函数暴露了太多依赖项(过度注入(,其唯一目的是允许单元测试。例如:

[ImportingConstructor]
public PasswordInputViewModel(
    IPaymentSystemProvider provider,
    IAppContext appCtx,
    IEventAggregator eventAggregator,
    IPromptCreator promptCreator) {
}
关键是其中

三个依赖项是来自基础结构的依赖项。公开它们只是为了显式注入,唯一的目的是允许单元测试,这比 ViewModel 依赖项的有用信息增加了更多的噪音。特别是,因为几乎所有 VM 都有这些依赖项。

因此,我们将这些依赖项移动到 BaseViewModel 类中:

public class ScreenExtended : Screen {
    [Import]
    public IEventAggregator eventAggregator { get; private set; }
    [Import]
    public IPromptCreator Prompt { get; private set; }
    [Import]
    public IAppContext CurrentApp { get; private set; }
}

现在,我们需要以某种方式从逃避构造函数注入的单元测试中模拟它们。因此,我们可以引导 IoC。

现在的问题是:如何在这里正确使用 IoC?为每个类或每个测试引导 IoC,还是将其设置为静态并仅初始化一次?如果我们将其设置为静态,则需要以某种方式重组 IoC。你会推荐什么?

如何在单元测试中正确使用 IoC

我想说这取决于注入的项目是具体的实现还是模拟对象。如果您使用的是具体的实现,那么如果您在类级别初始化,则测试之间可能会出现状态问题。

我通常会重新初始化每个测试的每个依赖项,甚至是一个具体的类(在模拟存储库之上模拟服务层(。在测试 CRUD 类型功能时,尽管需要更长的时间,但能够将每个测试重置为零肯定会有所帮助。而是有一个可以单独或以列表形式正确可靠地运行的测试。

有时,ViewModel的构造函数暴露了太多依赖项(过度注入(,唯一的意图是允许单元测试。

当依赖关系过多时,更有可能违反了单一责任原则。

在提供的ViewModel上下文中,具有 4 个依赖项是否违规?这个问题没有正确的答案(即主观问题(。应该有一个依赖平衡:不要太少,也不要太多。

关键是其中

三个依赖项是来自基础结构的依赖项。公开它们只是为了显式注入,唯一的目的是允许单元测试,这比 ViewModel 依赖项的有用信息增加了更多的噪音。特别是,因为几乎所有 VM 都有这些依赖项。

实施单一责任原则的建议非常抽象,因为需要查看更多ViewModel类(它们对依赖项的用法(:引入抽象层来封装高级操作的实现。这听起来可能太"大",但它可能只是一个隐藏高级操作(使用基础设施服务等(实现细节的门面。

现在,我们需要以某种方式从逃避构造函数注入的单元测试中模拟它们。因此,我们可以引导 IoC。

请考虑使用构造函数注入并在不使用具体的 DI 容器 API 的情况下实现单元测试,除非绝对需要。单元测试不应依赖于具体的 DI 容器的 API。