避免在可测试方法中创建存储库的架构

时间:2015-12-09 11:55:33

标签: c# unit-testing architecture

使用MVVM应用程序。每个ViewModel类都有一个构造函数,它接受一个Repository类,以便可以模拟它进行单元测试。

该应用程序旨在同时跨多个窗口进行操作。所以它包含一个数字" View"或"打开"样式方法,用于创建新的ViewModel并将它们放入新窗口。因为这些是通过UI触发的,所以它们通常位于现有的ViewModel中。例如:

public void ViewQuote(Quote quote)
{
    if (quote.CreatedOn == null)
    {
        quote.CreatedOn = DateTime.Now;
    }

    NavigationHelper.NewWindow(this, new QuoteViewModel(quote, new Repository()));
}

现在,该流控制语句看起来值得测试,以确保使用null CreatedOn日期传递的引号被赋值为1。但是,我的测试失败了,因为虽然父ViewModel有一个模拟的存储库,但是NewWindow方法会在其中创建一个带有真实存储库的新ViewModel。然后,当它在该类的构造函数中使用时会抛出错误。

有两个明显的选择。

一种方法是将日期分配拉出到一个独立的功能中进行测试。这会起作用,但对于它自己的功能来说似乎过于简单了。此外,如果我在整个应用程序中执行此操作,则可能会产生过多碎片,从而易于阅读。

另一种方法是以某种方式更改ViewModels的构造函数代码,使其不直接使用Repository。这可能是一个选择,但它不可能适用于所有可能的情况。

或者有没有第三种方法可以更好地设计它,以便我可以将模拟的存储库传递给我的新ViewModel的构造函数?

2 个答案:

答案 0 :(得分:3)

新的服务(或类似服务的对象,如存储库)是一种设计气味。您遇到的问题就是结果。

换句话说,您缺乏清晰明确的Composition Root

解决方案:使用适当的依赖注入

唯一干净的解决方案是通过构造函数注入服务。存储库通常具有比应用程序本身更短的生命周期,因此在这种情况下,您将注入能够创建存储库的工厂。

请注意,明确的依赖树是很好的设计,但使用诸如Autofac之类的DI框架只是实现这种设计的一种技术解决方案。您可以完全解决问题并创建一个干净的组合根,而无需使用DI框架。

因此,虽然这可能需要做很多工作,但您应该重新设计应用程序以获得明确的组合根。否则,您将一遍又一遍地遇到小问题,尤其是在测试领域。

答案 1 :(得分:-1)

您可以通过在public Repository NewRepository方法中添加ViewQuote属性来解决此问题,将其更改为:

public void ViewQuote(Quote quote)
{
    if (quote.CreatedOn == null)
    {
        quote.CreatedOn = DateTime.Now;
    }
    if(NewRepository == null) NewRepository = new Respository();
    NavigationHelper.NewWindow(this, new QuoteViewModel(quote, NewRepository));
}

然后在模拟中,只需确保在对该位代码运行测试之前分配了公共NewRepository属性。

不是很优雅,但在我看来它需要的变化最小。