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

本文关键字:存储 体系结构 创建 测试方法 | 更新日期: 2023-09-27 18:26:12

在MVVM应用程序上工作。每个ViewModel类都有一个构造函数,它接受一个Repository类,以便对其进行单元测试。

该应用程序被设计为同时在多个窗口中操作。因此,它包含了许多"View"或"Open"样式的方法,这些方法可以创建新的ViewModel并将其放置在新窗口中。因为这些都是通过UI触发的,所以它们通常在现有的ViewModels中。例如:

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

现在,这个流控制语句看起来值得测试,以确保用null CreatedOn日期传递的引号得到一个赋值。然而,我的测试失败了,因为尽管父ViewModel有一个模拟的Repository,但NewWindow方法会生成一个新的ViewModel,其中有一个真实的Repository。然后,当它在该类的构造函数中使用时,会抛出一个错误。

有两个明显的选择。

一种是将日期分配提取到一个独立的函数中进行测试。这是可行的,但就其自身功能而言,它似乎过于简单化了。另外,如果我在整个应用程序中都这样做,可能会造成太多的碎片,从而提高可读性。

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

或者还有第三种方法可以更好地设计它,这样我就可以将一个模拟的存储库传递到我的新ViewModel的构造函数中吗?

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

新建服务(或类似服务的对象,如存储库)是一种设计气味你所经历的问题就是后果。

换句话说,你缺乏一个清晰定义的复合根

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

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

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

因此,尽管这可能是一项艰巨的工作,但您应该重新设计您的应用程序,使其具有清晰的组合根。否则,您将一次又一次地遇到小问题,尤其是在测试领域

您可以通过添加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));
}

然后,在mock中,只需确保在对该代码执行测试之前,已将公共NewRepository属性分配给。

不是很优雅,但在我看来,这需要最少的改变。