0

我有一个方法将我的实体框架实体转换为 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 参数名称为 ArgumentNullException:source。

我有一个返回的存储库,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?

4

1 回答 1

1

你不会。如果您正在测试与 EF 相关的代码,则必须使用真实的 EF 和真实的数据库进行测试 - 没有办法解决这个问题,任何尝试避免它都等于不测试您的应用程序,而是假设 EF 应该如何工作。

为什么在你的情况下不可能伪造?CreateSourceQuery是一种方法,EntityCollection实体集合依赖于真实的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。例如,如果您的关系已经加载,会发生什么?或者,如果您的实体因某种原因脱离,会发生什么?这些是不容易测试的副作用。

于 2011-12-29T20:08:54.867 回答