简体   繁体   English

实体框架6单元测试(NSubstitute) - 如何测试带有孩子的对象在没有请求孩子时不返回孩子

[英]Entity Framework 6 Unit Testing (NSubstitute) - How to test that an object with children does not return children when children are not requested

I have a method to get an Employer using an Entity Framework context (Lazy Loading is disabled). 我有一个方法来使用实体框架上下文获取雇主(禁用延迟加载)。 Sometimes I want the Employees included, sometimes I don't so I have the following code in my data access class: 有时候我想要包含Employees,有时我不这样做,因此我的数据访问类中包含以下代码:

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;
}

From several questions about how to use NSubstitute to substitute EF context returns I have this extension method in my test project to hook up the DbSet calls for substitution (specifically NSubstitute DbSet / IQueryable<T> ): 从几个关于如何使用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;
}

This is then used to initialise a substitute set of Employers in the test class: 然后,它用于初始化测试类中的替代雇主集:

[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);
}

So, I now have a test that looks like this: 所以,我现在有一个看起来像这样的测试:

[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();
}

This test is failing on Assert.IsNull(actualEmployee); 此测试在Assert.IsNull(actualEmployee);上失败Assert.IsNull(actualEmployee);

In real usage, GetEmployer will return an Employer with no Employee children. 在实际使用中,GetEmployer将返回没有Employee子女的雇主。

However, because I am substituting an Employer with Employee (because this is what I am testing!) the method is returning the substitute which has an Employee. 但是,因为我用雇员代替雇主(因为这是我正在测试的!),该方法返回具有Employee的替代品。

How can I test this? 我该怎么测试呢?

Or, am I testing incorrectly? 或者,我测试不正确吗?

Should I instead use the Employer which doesn't have an Employee, because that is what the context would return? 我是否应该使用没有员工的雇主,因为这是上下文会返回的内容?

But then doesn't that make the test pointless!?! 但那不会使测试毫无意义!?!

I'm thinking myself in circles here... 我在这里思考自己......

Many times I have tried to mock out DbContext with different techniques. 很多时候我试图用不同的技术来模拟DbContext。 But every time when I thought "yeah, this time it behaves like real EF!" 但每当我想到“是的,这次它的表现就像真正的EF!” I have found yet another use-case when mock does not behave like a real thing. 我发现了另一个用例,当mock不像真实的东西那样。 An having tests that pass with mocks does give you a false confidence. 通过模拟进行的测试确实会让您产生错误的信心。 But when the same actions happen in production, you get exceptions and errors. 但是当生产中发生相同的操作时,您会遇到异常和错误。

So my conclusion was to stop trying to mock DbContext and just do integration tests. 所以我的结论是停止尝试模拟DbContext并进行集成测试。 It is a bit problematic to set up, but setting up realistic mock takes even more time! 设置有点问题,但设置逼真的模拟需要更多的时间! And I've written about how to have trouble-free integration tests in my blog: http://tech.trailmax.info/2014/03/how-we-do-database-integration-tests-with-entity-framework-migrations/ 我写过关于如何在我的博客中进行无故障集成测试的文章: http//tech.trailmax.info/2014/03/how-we-do-database-integration-tests-with-entity-framework-迁移/

Now I tend to write a lot of integration tests (for CRUD stuff) that actually go into DB and mess about. 现在我倾向于编写很多集成测试(对于CRUD的东西),这些测试实际上是进入DB而且很糟糕。 Gives you much more assurance (rather than using db-mock) that the same actions will work in production. 为您提供更多保证(而不是使用db-mock),相同的操作将在生产中起作用。

Not really an answer to your question. 不是你的问题的答案。 Just my .02$ 只是我的.02 $

This is not possible. 这是不可能的。 It is a limitation of the EF "in-memory" testing model. 这是EF“内存中”测试模型的限制。 See this article for details: 有关详细信息,请参阅此文

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

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

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