Entity-Framework. Testing that SaveChanges is present and called in the correct place in a method

c# entity-framework mocking unit-testing

Question

I want to unit test a class that looks like this:

public class AddUserCommand
{
    IDbContext dbContext;

    public AddUserCommand(IDbContext context)   
    {
    dbContext = context;
    }

    public void Execute()
    {
        dbContext.Users.Add(new User());
        dbContext.SaveChanges();
    }
}

In the end, I need to check to see if, while utilizing a genuine SQL database connection, the Execute method persists the new User to the database. But I definitely want to utilize a dummy object in my unit tests. I can create a mock IDbContext that mirrors the behavior in my tests, and everything works. After the Execute method has been called, I can verify that the mock context has the new user.

My issue is that if I don't call the SaveChanges method when utilizing the mock context, the test will pass. This is so that the data can truly be persistent in the fake environment without requiring a SQL query. Due to the fact that the Users collection serves as the persistent storage, it "persists" even without a SaveChanges method.

Numerous web sources (such as http://msdn.microsoft.com/en-us/library/ff714955.aspx and http://msdn.microsoft.com/en-gb/data/dn314431.aspx) advise adding the following to the mock context to see if SaveChanges is called:

public class MockDbContext : IDbContext
{
    boolean saved;
    public void SaveChanges {
        saved = true;
    }
}

After calling the Execute method, check to see if the saved value is true. But what I don't like about this strategy is that a test like this will pass if the Execute function does the following:

public void Execute()
{
    dbContext.SaveChanges();
    dbContext.Users.Add(new User());
}

Of course, this would not save any modifications because it is completed too soon. I think that testing the sequence of method calls to the mock context is possible with mocking frameworks like RhinoMocks, but I have also read that this is not recommended (you should test the result, not the minutae of the implementation).

The mock context's inability to precisely mimic what the real DbContext will accomplish is the problem.

Therefore, I want to know if there is a standard approach to fake an entity framework DbContext so that any object updates or deletions are only applied to the mock when SaveChanges is invoked. Or is there a reason why this isn't typically tested for?

1
7
2/4/2014 2:23:23 PM

Popular Answer

Using the Moq structure, you ought to be able to perform this:

// Counters to verify call order
int callCount = 0;
int addUser = 0;
int saveChanges = 0;

// use Moq to create a mock IDbContext.
var mockContext = new Mock<IDbContext>();

// Register callbacks for the mocked methods to increment our counters.
mockContext.Setup(x => x.Users.Add(It.IsAny<User>())).Callback(() => addUser = callCount++);
mockContext.Setup(x => x.SaveChanges()).Callback(() => saveChanges = callCount++);

// Create the command, providing it the mocked IDbContext and execute it
var command = new AddUserCommand(mockContext.Object);
command.Execute();

// Check that each method was only called once.
mockContext.Verify(x => x.Users.Add(It.IsAny<User>()), Times.Once());
mockContext.Verify(x => x.SaveChanges(), Times.Once());

// check the counters to confirm the call order.
Assert.AreEqual(0, addUser);
Assert.AreEqual(1, saveChanges);

According to the comments left on this response, some people appear to be ignorant of the benefits of unit testing and the use of abstractions in programming.

Verifying the actions of the is what you are doing in this situation.AddUserCommand simply by doing so, you are attesting to theAddUserCommand class is including a user and preserving the context changes.

The purpose of using theIDbContext so that you can test the interfaceAddUserCommand class without a known-state database available, in isolation. You are not required to test the actual implementation of theDbContext since those should be covered by independent unit tests as well.

You might want to develop an integration test in which you would utilize the actualDbContext and verify that a record is added to the database, although a unit test already does that.

13
2/4/2014 4:00:49 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow