Rhino mock -期望/stub从外部依赖中获得非虚拟方法

本文关键字:虚拟 方法 依赖 -期望 mock stub 从外部 Rhino | 更新日期: 2023-09-27 18:10:27

我正在用Rhino mock为c#编写的WPF应用程序编写一些单元测试,该应用程序使用Unity进行依赖注入并使用MVVM架构。我对Rhino mock的单元测试不是很有经验,所以我不确定最佳实践是什么。

在视图模型中,我要写单元测试,有一个依赖注入的数据访问类,我们称之为DataAccess,那是来自一个外部程序集,我不控制。只有一个实例被注册到Unity容器中,因为DataAccess有一个缓存,为了提高性能,我们希望通过Unity容器在整个应用程序中共享这个实例。现在我显然需要在单元测试中模拟DataAccess,因为我无法控制数据库中的数据。我希望存根或期望Retrieve方法返回一个特定的值,但是DataAccess没有实现接口,而且我需要存根的方法不是虚拟的。从我在网上看到的内容来看,Rhino mock不能覆盖非虚拟方法,唯一的其他选择是用您想要覆盖的方法模拟接口。这两个选项对于本例都无效,因为我不拥有DataAccess代码。我听说TypeMock有覆盖非虚拟方法的能力,但说服我的公司切换到付费的mock库可能不会发生,所以我被Rhino mock困住了。那么是否有一种方法可以模拟这个类并用Rhino mock覆盖这个方法呢?

public class DataAccess //in an external assembly
{
    public TEntity Retrieve(TKey key);
}
public class ViewModel //in the client project
{
    [Dependency]
    public DataAccess DataAccess { get; set; }
}

我想出了一个可能的解决方案,但它不使用mock,我想使用mock,因为有很多地方我们有这种情况。我的想法是为DataAccess创建一个包装器类(假的),它具有与DataAccess相同的方法和属性,只是所有重要的方法/属性都被标记为virtual,我将把这个类放在我的测试项目中。然后,在单元测试初始化器中,我将注册一个从DataAccess到Unity容器中的包装器类的类型映射,以便在单元测试中实例化的VM将获得包装器类的实例。除了创建一堆包装器类之外,您还看到沿着这个方向前进的任何缺点吗?

public class DataAccessFake : DataAccess //in the test project
{
    public new virtual TEntity Retrieve(TKey key) //hides the DataAccess Retrieve with a virtual one
    {
        return base.Retrieve(key);
    }
}

Rhino mock -期望/stub从外部依赖中获得非虚拟方法

下面是我测试过的一些代码,以表明所建议的方法不起作用:

class Program
{
    static void Main(string[] args)
    {
        ViewModel vm = new ViewModel()
        {
            DataAccess = new DataAccessFake()
        };
        string result = vm.Test("test"); //this returns "test_original"
    }
}
public class ViewModel
{
    public DataAccess DataAccess { get; set; }
    public string Test(string key)
    {
        return DataAccess.Retrieve(key);
    }
}
public class DataAccess
{
    public string Retrieve(string key)
    {
        return key + "_original";
    }
}
public class DataAccessFake : DataAccess
{
    public new virtual string Retrieve(string key)
    {
        return key + "_fake";
    }
}

如果您运行这段代码,测试方法的输出将是"test_original",这表明DataAccessFake。从未调用检索方法。您也可以在这样的方法中放置一个断点来验证。

由于ViewModel依赖于DataAccess,它将始终调用DataAccess。这是一个与DataAccessFake.Retrieve完全不同的方法。

当然,如果DataAccessFake。