我使用EF 4.1构建域模型.我有一个具有Validate(string userCode)方法的Task类,其中我想确保用户代码映射到数据库中的有效用户,因此:
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没问题.但是遇到麻烦的地方呼叫:
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查询需要很频繁,所以服务对象可以迅速地失控.
有可能有一点灵魂有帮助吗?谢谢!
据我所知,Moq只能设置虚拟对象本身的虚拟方法,但您正在尝试设置扩展(静态)方法 – 没有办法!这些方法绝对不在您的模拟范围之内.
此外,代码错误,难以测试.您需要进行大量初始化才能测试.改用:
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以返回您的列表.这样的测试有一些重大问题:
>你没有测试真正的实现 – 如果EF嘲笑集是愚蠢的方法,因为一旦你这样做,你将linq-to-entity更改为linq-to-objects.这两个是完全不同的,linq到实体只是linq-to-objects的一小部分,你的单元测试可以通过linq-to-object传递,但你的代码在运行时会失败.
>一旦你使用这种方法,你不能使用Include,因为include取决于DbQuery / DbSet.再次,您需要集成测试才能使用它.
>这不会测试你的懒惰加载工作
更好的方法是从Validate方法中删除您的LINQ查询 – 只需将它们称为对象的另一种虚拟方法即可.单元使用嘲弄的查询方法测试您的Validate方法,并使用集成测试来自行测试查询.