Moq测试LINQ Where查询 [英] Moq testing LINQ Where queries

查看:128
本文介绍了Moq测试LINQ Where查询的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用EF 4.1建立域模型.我有一个带有Validate(string userCode)方法的Task类,在其中我想确保用户代码映射到数据库中的有效用户,所以:

I'm using EF 4.1 to build a domain model. I have a Task class with a Validate(string userCode) method and in it I want to ensure the user code maps to a valid user in the database, so:

public static bool Validate(string userCode)
{
    IDbSet<User> users = db.Set<User>();
    var results = from u in users
              where u.UserCode.Equals(userCode)
              select u;
    return results.FirstOrDefault() != null;
}

我可以使用Moq模拟IDbSet没问题.但是在Where调用中遇到了麻烦:

I can use Moq to mock IDbSet no problem. But ran into trouble with the Where call:

User user = new User { UserCode = "abc" };
IList<User> list = new List<User> { user };
var users = new Mock<IDbSet<User>>();
users.Setup(x => x.Where(It.IsAny<Expression<Func<User, bool>>>())).Returns(list.AsQueryable);

Initialization method JLTi.iRIS3.Tests.TaskTest.SetUp threw exception.
System.NotSupportedException: System.NotSupportedException: Expression 
references a method that does not belong to the mocked object:
x => x.Where<User>(It.IsAny<Expression`1>()).

除了创建间接级别(例如,使用ServiceLocator获取运行LINQ的对象然后模拟该方法)之外,我无法考虑如何对此进行测试,但是我想确保有在介绍另一层之前没办法.而且我可以看到经常需要这种LINQ查询,以便服务对象迅速失控.

Other than creating a level of indirection (eg, using a ServiceLocator to get an object that runs the LINQ and then mock that method) I can't think of how else to test this, but I want to make sure there is no way before I introduce another layer. And I can see this kind of LINQ queries will be needed quite often so the service objects can quickly spiral out of control.

某种善良的灵魂能帮上忙吗?谢谢!

Could some kind soul help? Thanks!

推荐答案

据我所知Moq只能设置模拟对象本身的虚拟方法,但是您正在尝试设置扩展(静态)方法-没办法!这些方法绝对不在您的模拟范围之内.

As I know Moq is able to set up only virtual methods of mocked object itself but you are trying to set up extensions (static) method - no way! These methods are absolutely outside of your mock scope.

此外,该代码很难测试,并且需要太多的初始化才能对其进行测试.改用它:

Moreover that code is hard to test and requires too much initialization to be able to test it. Use this instead:

internal virtual IQueryable<User> GetUserSet()
{
    return db.Set<User>();
} 

public bool Validate(string userCode)
{
    IQueryable<User> users = GetUserSet();
    var results = from u in users
                  where u.UserCode.Equals(userCode)
                  select u;
    return results.FirstOrDefault() != null;
}

您只需要设置GetUserSet即可返回列表.这样的测试存在一些主要问题:

You will just need to set up GetUserSet to return your list. Such testing has some major issues:

  • 您没有测试真正的实现-如果EF模拟集是愚蠢的方法,因为一旦完成,您就将linq-to-entities更改为linq-to-objects.这两个完全不同,并且linq-to-entities只是linq-to-objects的一小部分=您的单元测试可以通过linq-to-objects通过,但是您的代码将在运行时失败.
  • 一旦使用此方法,就不能使用Include,因为include依赖于DbQuery/DbSet.同样,您需要集成测试才能使用它.
  • 这不会测试您的延迟加载是否有效
  • You are not testing the real implementation - in case of EF mocking sets is stupid approach because once you do it you change linq-to-entities to linq-to-objects. Those two are totally different and linq-to-entities is only small subset of linq-to-objects = your unit tests can pass with linq-to-objects but your code will fail at runtime.
  • Once you use this approach you cannot use Include because include is dependent on DbQuery / DbSet. Again you need integration test to use it.
  • This doesn't test that your lazy loading works

更好的方法是从Validate方法中删除您的linq查询-只需将它们作为对象的另一个虚拟方法调用即可.使用模拟的查询方法对您的Validate方法进行单元测试,并使用集成测试来测试查询本身.

The better approach is removing your linq queries from Validate method - just call them as another virtual method of the object. Unit test your Validate method with mocked query methods and use integration tests to test queries themselves.

这篇关于Moq测试LINQ Where查询的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆