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

C# 如何在单元测试中正确使用IoC?,c#,wpf,unit-testing,c#-4.0,mef,C#,Wpf,Unit Testing,C# 4.0,Mef,我们有一个WPF应用程序。 许多ViewModel都使用必须模拟的相同依赖项。 有时,ViewModels的构造函数有太多的依赖项(过度注入),只是为了允许单元测试而暴露。 例如: [ImportingConstructor] public PasswordInputViewModel( IPaymentSystemProvider provider, IAppContext appCtx, IEventAggregator eventAggregator, IP

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

[ImportingConstructor]
public PasswordInputViewModel(
    IPaymentSystemProvider provider,
    IAppContext appCtx,
    IEventAggregator eventAggregator,
    IPromptCreator promptCreator) {
}
关键是其中三个依赖项是来自基础架构的依赖项。仅仅为了显式注入而公开它们,只是为了允许单元测试,这比ViewModel的依赖关系的有用信息增加了更多的噪音。特别是,因为几乎所有虚拟机都有这些依赖关系

因此,我们将这些依赖项移动到
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,或者使其静态并只初始化一次?如果我们让它成为静态的,那么我们需要以某种方式重新组合国际奥委会。您推荐什么?

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

我通常会在每个测试中重新初始化每个依赖项,甚至是一个具体的类(它在模拟存储库的顶部模拟服务层)。在测试CRUD类型的功能时,能够在每次测试中重置为零肯定会有所帮助,尽管这需要更长的时间。而是要有一个能够在隔离或列表中正确可靠地运行的测试

有时候,
ViewModel
s的构造函数有太多的依赖项(过度注入),它们只是为了允许单元测试而暴露出来

当存在太多依赖项时,则更可能违反了单一责任原则

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

关键是其中三个依赖项是来自基础架构的依赖项。仅仅为了显式注入而公开它们,只是为了允许单元测试,这比ViewModel的依赖关系的有用信息增加了更多的噪音。特别是,因为几乎所有虚拟机都有这些依赖关系

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

现在,我们需要从单元测试中模拟它们,避免构造函数注入。因此,我们可以引导国际奥委会


请考虑使用构造函数注入和实现单元测试而不使用具体的DI容器API,除非它是绝对必需的。单元测试不应该依赖于具体DI容器的API。

因此,当您编写单元测试时,您需要为每个测试重新初始化IoC,对吗?或者您只是删除依赖项的以前的实现者并添加一个新的实现者?是的,每次测试都要重新初始化容器。您是否依靠自己的经验和实践,或者您是否已经在某个地方看到了最佳实践?据我所知,在单元测试中,IoC的引导程序有两种方法,你可以通过它们来传递模拟和一对方法,如Create和Disponse,是吗?据我所知,在单元测试中,IoC的引导程序有两种方法,你可以通过它们来传递模拟和一对方法,如Create(容器的组成位置)和Dispose,是吗?我使用Unity而不是MEF作为IoC容器。我预先加载了容器中所需的实例,然后根据需要将容器传递给我的VM(这将使用ServiceLocator模式解析它们所需的任何依赖项)。