3

我有一种使用实体框架上下文获取雇主的方法(延迟加载已禁用)。有时我希望包括员工,有时我不希望在我的数据访问类中有以下代码:

public Employer GetEmployer(int employerId, bool includeRelationships)
{
    Employer employer;

    if (includeRelationships)
    {
        employer = (from e in this.context.Employers.Include(e => e.Employees)
                    where e.EmployerId == employerId
                    select e).SingleOrDefault();
    }
    else
    {
        employer = this.context.Employers.SingleOrDefault(e => e.EmployerId == employerId);
    }

    return employer;
}

从关于如何使用 NSubstitute 替换 EF 上下文返回的几个问题中,我在我的测试项目中有这个扩展方法来连接 DbSet 调用以进行替换(特别是NSubstitute DbSet / IQueryable<T>):

public static IDbSet<T> Initialise<T>(this IDbSet<T> dbSet, IQueryable<T> data) where T : class
{
    dbSet.Provider.Returns(data.Provider);
    dbSet.Expression.Returns(data.Expression);
    dbSet.ElementType.Returns(data.ElementType);
    dbSet.GetEnumerator().Returns(data.GetEnumerator());
    return dbSet;
}

然后使用它来初始化测试类中的一组替代雇主:

[TestInitialize]
public void TestInitialise()
{
    this.context = Substitute.For<EmployerContext>();

    this.dao = new EmployerDao(this.context);

    var employers = new List<Employer>();

    var employerWithoutEmployee = new Employer { EmployerId = 1 };

    employers.Add(employerWithoutEmployee);

    var employerWithEmployee = new Employer { EmployerId = 2 };

    var employee = new Employee { EmployeeId = 1, EmployerId = 2, Employer = employerWithEmployee };

    employerWithEmployee.Employees.Add(employee);

    employers.Add(employerWithEmployee);

    this.substituteEmployers = Substitute.For<IDbSet<Employer>>().Initialise(employers.AsQueryable());

    this.context.Employers.Returns(this.substituteEmployers);
}

所以,我现在有一个看起来像这样的测试:

[TestMethod]
public void ReturnsEmployerWithNullEmployeeWhenIncludeIsFalse()
{
    // Assemble
    var expectedEmployer = this.substituteEmployers.First(e => e.Employees.Any();

    var employerId = expectedEmployer.EmployerId;

    // Act
    var actualEmployer = this.dao.GetEmployer(employerId, false);

    var actualEmployee = actualEmployer.Employees.FirstOrDefault();

    // Assert
    Assert.AreSame(expectedEmployer, actualEmployer);
    Assert.IsNotNull(actualEmployer);
    Assert.IsNull(actualEmployee);
    this.context.Employers.ReceivedWithAnyArgs();
}

此测试失败Assert.IsNull(actualEmployee);

在实际使用中,GetEmployer 将返回一个没有 Employee 孩子的 Employer。

但是,因为我用 Employee 替换了 Employer(因为这是我正在测试的!)该方法返回具有 Employee 的替换。

我该如何测试呢?

或者,我测试不正确?

我是否应该改用没有员工的雇主,因为这是上下文将返回的内容?

但这不是让测试毫无意义吗!?!

我在想自己在这里圈子......

4

2 回答 2

3

很多次我都试图用不同的技术来模拟 DbContext。但是每次当我想“是的,这次它的表现就像真正的 EF!” 当模拟的行为不像真实的东西时,我发现了另一个用例。一个通过模拟通过的测试确实会给你一种错误的信心。但是,当生产中发生相同的操作时,您会遇到异常和错误。

所以我的结论是停止尝试模拟 DbContext 并只进行集成测试。设置起来有点问题,但设置逼真的模拟需要更多时间!我已经在我的博客中写过如何进行无故障集成测试:http: //tech.trailmax.info/2014/03/how-we-do-database-integration-tests-with-entity-framework-迁移/

现在我倾向于编写很多集成测试(用于 CRUD 的东西),这些测试实际上会进入数据库并搞砸。为您提供更多保证(而不是使用 db-mock)相同的操作将在生产中起作用。

不是你的问题的真正答案。只是我的 .02$

于 2014-07-02T00:44:34.240 回答
0

这是不可能的。这是 EF“内存中”测试模型的限制。详情请看这篇文章:

https://msdn.microsoft.com/en-us/data/dn314429#limitations

于 2015-12-02T22:16:36.857 回答