繁体   English   中英

单元测试时,在Navigation属性上调用.CreateSourceQuery()会返回null; 在实际数据库上工作,如何设置测试数据以匹配?

[英]Calling .CreateSourceQuery() on Navigation Property returns null when Unit Testing; works on actual database, how do I setup my test data to match?

我有一个方法将实体框架实体转换为DTO对象。 在这种方法中,我有一些参数可以跳过并限制要返回的相关项目的数量。 对于小型数据集,像这样的简单查询非常有效:

var query = this.AccessLogs
    .Skip(skipRelated)
    .Take(takeRelated);

对于较大的数据集,我发现它实际上在我的数据库上执行了SELECT *,并且由于在某些情况下具有数百万条相关的记录而导致了很多问题。 问完这个问题后,我将查询修改为:

var query = this.AccessLogs
    .CreateSourceQuery()
    .OrderBy(p => p.ID)
    .Skip(skipRelated)
    .Take(takeRelated);

现在,虽然这解决了我在集成测试中遇到的性能问题,但是这导致我的每个单元测试都失败了,因为.CreateSourceQuery()返回null,然后我的.OrderBy() barfs带有参数名称:source的ArgumentNullException。

我有一个返回IQueryable<T>的存储库,并且具有依赖项注入设置来对其进行单元测试,因此我要像这样设置“测试”数据。 最初,我只是使用List<T>但是我发现本文使用InMemoryObjectSet<T>进行测试。 无论哪种方式,我打电话给.CreateSourceQuery()返回null时,即使底层集合中的数据。

IObjectSet<Parent> ret = new InMemoryObjectSet<Parent>();
var parent = new Parent();
parent.ID = 1;
parent.Name = "Name 1";
for(int i = 0; i < 5; i++)
{
    var ch = new Child();
    ch.ID = i;
    ch.ParentID = 1;
    ch.Property1 = "Name " + i.ToString();
    parent .Children.Add(ch);
}
ret.AddObject(parent);

我的问题是:如何设置单元测试的测试数据,以使.CreateSourceQuery()不返回null?

你不会。 如果您要测试与EF相关的代码,则必须使用真实的EF和真实的数据库进行操作-无法解决此问题,任何避免它的尝试都等于不测试您的应用程序,而是对EF应该如何工作进行一些假设。

为什么在您的情况下无法伪造? CreateSourceQueryEntityCollection一种方法,而实体集合依赖于实际的ObjectContext。 同时EntityCollection被密封。 该方法在任何公共接口中也不可用。 因此,无法用普通的单元测试API替换其逻辑。 唯一的选择是使用一些更先进的技术,该技术将使您可以将方法调用重定向到其他方法(仅由商业TypeMock Isolator和MS Moles提供),但这将导致与您当前尝试的问题相同的问题。 =>测试关于您不拥有的代码的假设。 仅当您的测试未测试与EF相关的代码,查询或持久性时,任何类型的伪造才有意义-这些内容必须由单独的集成测试覆盖。

如何避免呢? 在经过测试的类中,创建新方法:

protected virtual IEnumerable<AcessLog> GetLogs(int skipRelated, int takeRelated)
{
     return this.AccessLogs.
                .CreateSourceQuery()
                .OrderBy(a => p.ID)
                .Skip(skipRelated)
                .Take(takeRelated);
}

现在,在您的测试中,不要使用原始类,而是使用派生类,该类以某种方式覆盖GetLogs方法并返回您在测试中期望的结果。

可是等等。 我刚刚跳过了在GetLogs对您的逻辑的测试,不是吗? 是的,我确实做到了。 如上所述,该代码无法进行单元测试。 必须使用真实数据库进行单独的集成测试来覆盖它,但是您必须将代码隔离在单个方法中,并且可以通过伪造该方法来对依赖于此方法的所有其他逻辑进行单元测试。

它仍然不能解决CreateSourceQuery可能遇到的所有问题。 例如,如果您的关系已经加载,会发生什么? 或者,如果您的实体由于某种原因而脱离实体,该怎么办? 这些是不容易测试的副作用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM