如何在单元测试中正确使用 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。你会推荐什么?
我想说这取决于注入的项目是具体的实现还是模拟对象。如果您使用的是具体的实现,那么如果您在类级别初始化,则测试之间可能会出现状态问题。
我通常会重新初始化每个测试的每个依赖项,甚至是一个具体的类(在模拟存储库之上模拟服务层(。在测试 CRUD 类型功能时,尽管需要更长的时间,但能够将每个测试重置为零肯定会有所帮助。而是有一个可以单独或以列表形式正确可靠地运行的测试。
有时,
ViewModel
的构造函数暴露了太多依赖项(过度注入(,唯一的意图是允许单元测试。
当依赖关系过多时,更有可能违反了单一责任原则。
在提供的ViewModel
上下文中,具有 4 个依赖项是否违规?这个问题没有正确的答案(即主观问题(。应该有一个依赖平衡:不要太少,也不要太多。
关键是其中三个依赖项是来自基础结构的依赖项。公开它们只是为了显式注入,唯一的目的是允许单元测试,这比 ViewModel 依赖项的有用信息增加了更多的噪音。特别是,因为几乎所有 VM 都有这些依赖项。
实施单一责任原则的建议非常抽象,因为需要查看更多ViewModel
类(它们对依赖项的用法(:引入抽象层来封装高级操作的实现。这听起来可能太"大",但它可能只是一个隐藏高级操作(使用基础设施服务等(实现细节的门面。
现在,我们需要以某种方式从逃避构造函数注入的单元测试中模拟它们。因此,我们可以引导 IoC。
请考虑使用构造函数注入并在不使用具体的 DI 容器 API 的情况下实现单元测试,除非绝对需要。单元测试不应依赖于具体的 DI 容器的 API。