如何为存储库设置Moq

时间:2014-02-26 11:38:59

标签: c# unit-testing moq repository-pattern

我有方法

public IEnumerable<string> LoadCountries()
{
     try
     {
         return GetAll().GetCountries();
     }
     catch (Exception)
     {
          return null;
     }
 }

GetAll()方法从sql server加载数据但我在测试中不需要它 所以我在IEnumerable客户上更改了数据库。我想测试它

Mock<ICustomersRepository> mock = new Mock<ICustomersRepository>();
mock.Setup(m => m.GetAll()).Returns(customers.AsQueryable()); //change data
IEnumerable<string> countries = mock.Object.LoadCountries();
Assert.AreEqual(countries.Count(), 6); //expect 6 countries

但是在这个测试中countries.Count()== 0; 当然我可以修改模拟设置并添加

mock.Setup(m => m.LoadCountries()).Returns(customers.AsQueryable().GetCountries());

但我不想这样做,因为我想测试的其他功能要大得多。 有谁知道如何设置GetAll()函数来返回我的测试数组,即使GetAll()在测试类中实现? 我只是尝试设置

mock.CallBase = true;
mock.Setup(m => m.LoadCountries()).CallBase();

但我只看到异常

  

System.NotImplementedException:这是DynamicProxy2错误:   拦截器试图“继续”进行方法
  'System.Collections.Generic.IEnumerable`1 [System.String] LoadCountries()'没有目标。   在没有目标的情况下调用方法时,没有实现“继续”到和   拦截器的责任是模仿实现(设置返回   价值,参数等)。

2 个答案:

答案 0 :(得分:1)

您正在创建实现ICustomersRepository接口的dymic代理类。然后你正在运用这个生成的代理:

Mock<ICustomersRepository> mock = new Mock<ICustomersRepository>();
IEnumerable<string> countries = mock.Object.LoadCountries();

这不是一个好主意。您通过此测试验证了什么?代理生成?代理不会被您的真实代码使用。您应该使用模拟来提供模拟的依赖项,以测试应用程序使用的实际类。

实际上我不会在内部测试LoadCountries()调用GetAll()。因为这是实现的细节,而不是客户存储库的业务要求。我会写一些验收/集成测试来验证给定客户返回正确的国家。

更新:因此,您的GetCountries()过滤器是一个扩展,然后您不需要在其测试中涉及存储库。这是一个简单的静态类,可以单独测试。 您的测试没有与数据库交互也不会很有价值,因为您的扩展组成了对数据源的查询,这将由查询提供程序翻译。我给你举个例子。如果您有本地方法调用(或您的查询提供程序可以翻译的方法):

public static class Extensions
{
    public static IQueryable<string> GetCountries(this IQueryable<Customer> q) 
    {
        return q.GroupBy(c => c.Country).Select(x => LocalMethod(x.Key)); 
    }

    private static string LocalMethod(string country)
    {
        return country.ToUpper();
    }
}

当您处理内存中的对象时,将继续执行单元测试:

List<Customer> customers = new List<Customer> {
    new Customer { Country = "USA" },
    new Customer { Country = "Spain" }
};

var countries = Extensions.GetCountries(customers.AsQueryable());
Assert.AreEqual(countries.Count(), 2);

但是当您运行使用Entity Framework查询SQL数据库的应用程序时,EF将无法将此过滤器转换为SQL。

答案 1 :(得分:0)

但是如果您正在尝试测试您的存储库并且您的LoadCountries位于您的存储库层中,那么您不应该模拟您的存储库。

你不能在模拟对象上使用存储库方法;)至少没有指定一些Return()

如果你模拟你的repo是因为你不想连接到testa dn的数据库,你想要使用例如:

来检索模拟数据
List<Country> myList = //Fill your list with countries
repo.Setup(x => x.LoadCountries).Return(myList);

然后,使用其他部分来使用此模拟数据。

但正如我所说,如果你试图测试你的存储库,你不应该嘲笑它。

当您尝试例如测试某些业务部分时,您会模拟存储库,然后您需要来自数据库的一些虚假数据,模拟存储库,并获取虚假数据;)

如果是这种情况,那么:

Mock<ICustomersRepository> mock = new Mock<ICustomersRepository>();
mock.Setup(x => x.LoadCountries).Return(myList);

然后例如,如果您从业务层中在构造函数中询问ICustomersRepository,那么您可以在业务层创建一个实例并传递此模拟仓库。

MyService service = new MyService(mock.Object);
service.LoadAll();

如果这个LoadAll()使用LoadCountries,那么当这个LoadAll调用LoadCountries时,它将获得你的国家的模拟数据!

我希望这有帮助!