Rhino Mocks - 期望/存根来自外部依赖的非虚方法

时间:2015-08-28 21:19:09

标签: c# mvvm dependency-injection unity-container rhino-mocks

我正在用Rhino Mocks编写一些单元测试用于用C#编写的WPF应用程序,该应用程序使用Unity进行依赖注入并使用MVVM achitechture。我对Rhino Mocks的单元测试经验不足,所以我不确定最佳实践是什么。

在我要编写单元测试的视图模型中,有一个依赖注入数据访问类,我们称之为DataAccess,即来自我不喜欢的外部程序集。控制。 Unity容器中只注册了一个实例,因为DataAccess具有缓存,并且希望通过Unity容器在整个应用程序中共享该实例,以提高性能。现在我需要在单元测试中模拟DataAccess,因为我无法控制数据库中的数据。我想存根或期望Retrieve方法返回一个特定的值,但DataAccess没有实现一个接口,我需要存根的方法不是虚拟的。从我在线阅读的内容来看,Rhino Mocks无法覆盖非虚方法,唯一的另一种选择是使用您想要覆盖的方法来模拟接口。这些选项都不适用于这种情况,因为我不拥有DataAccess代码。我听说TypeMock能够覆盖非虚拟方法,但说服我的公司切换到付费模拟库可能不会发生,所以我坚持使用Rhino Mocks。那么有没有办法模拟这个类并用Rhino Mocks覆盖这个方法?

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; }
}

我提出了一个可能的解决方案,但它没有使用模拟,我想使​​用模拟,因为有很多地方我们都有这种情况。我的想法是为DataAccess创建一个包装类(假),它具有与DataAccess相同的方法和属性,除了所有重要的方法/属性都标记为虚拟,我将该类放在我的测试项目中。然后在单元测试初始化​​器中,我将注册从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);
    }
}

1 个答案:

答案 0 :(得分:0)

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

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方法的输出将为“test_original”,表明从未调用过DataAccessFake.Retrieve方法。您还可以在此类方法中放置断点以进行验证。

由于ViewModel依赖于DataAccess,它总是会调用DataAccess.Retrieve,这是一种与DataAccessFake.Retrieve完全不同的方法。

当然,如果DataAccessFake.Retrieve是DataAccess.Retrieve的重写方法,则行为会有很大不同。