在单元测试时,对导航属性调用.CreateSourceQuery()返回null;在实际的数据库上工作,我如何设置我的测

本文关键字:数据库 工作 我的 设置 何设置 导航 单元测试 属性 调用 null 返回 | 更新日期: 2023-09-27 18:18:59

我有一个方法,它是采取我的实体框架实体,并将其转换为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()在参数名称: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?

在单元测试时,对导航属性调用.CreateSourceQuery()返回null;在实际的数据库上工作,我如何设置我的测

你不会的。如果你在测试EF相关的代码,你必须使用真实的EF和真实的数据库——没有办法绕过这一点,任何试图避免它的尝试都等于不测试你的应用程序,而是对EF应该如何工作的一些假设。

为什么在你的情况下不可能伪造?CreateSourceQueryEntityCollection的一个方法,实体收集依赖于真实的ObjectContext。同时,EntityCollection被密封。此方法在任何公共接口中也不可用。所以没有办法用普通的单元测试api来替换它的逻辑。唯一的选择是使用一些更先进的技术,允许您将方法调用重定向到其他方法(这仅由商业TypeMock Isolator和MS mole提供),但这将导致与您目前试图做的相同的问题=>测试您不拥有的代码的假设。只有当你的测试不测试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的所有问题。例如,如果您的关系已经加载,会发生什么?或者如果你的实体因为某种原因被分离了怎么办?这些都是不容易测试的副作用。