3
votes

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:

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>):

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

In real usage, GetEmployer will return an Employer with no Employee children.

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.

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...

2
Been a while since I used EF, but isn't this the default behavior? (i.e. virtual child collections are lazy-loaded automatically). - George Howarth
I've specifically switched off Lazy Loading... I'll update the question. - Shevek
It looks like you haven't set it in your [TestInitialize] method: this.context.Configuration.LazyLoadingEnabled = false; - George Howarth
Still fails when on the same line. When I debug the Employer has an Employee straight after the employer = this.context.Employers.SingleOrDefault(e => e.EmployerId == employerId); line in the dao method. its passing the substitute straight through - Shevek
Unless it's employerWithEmployee.Employees.Add(employee); that's messing it up. By doing that, it's setting Employees to a non-null value and thus mimicking the behavior of a context which has already lazily loaded that collection. Sorry, kind of clutching at straws here... - George Howarth

2 Answers

3
votes

Many times I have tried to mock out DbContext with different techniques. But every time when I thought "yeah, this time it behaves like real EF!" I have found yet another use-case when mock does not behave like a real thing. 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. 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/

Now I tend to write a lot of integration tests (for CRUD stuff) that actually go into DB and mess about. Gives you much more assurance (rather than using db-mock) that the same actions will work in production.

Not really an answer to your question. Just my .02$

0
votes

This is not possible. It is a limitation of the EF "in-memory" testing model. See this article for details:

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