Search code examples
c#unit-testingentity-framework-corein-memory-database

Testing against Entity Framework InMemory


I'm currently testing an Entity Framework's DbContext using the In-Memory Database.

In order to make tests as atomic as possible, the DbContext is unique per test-method, and it's populated with initial data needed by each test.

To set the initial state of the DbContext, I've created a void SetupData method that fills the context with some entities that I will use in the tests.

The problem with this approach is that the objects that are created during the setup cannot be accessed by the test, because Entity Framework will assign the Ids itself, that are unknown until run-time.

To overcome this problem, I've thought that my SetupData method could become something like this:

public Fixture SetupData(MyContext context) 
{
    var fixture = new Fixture();
    fixture.CreatedUser = new User();
    context.Users.Add(fixture.CreatedUser);
    context.SaveChanges();

    return fixture;
}

public class Fixture 
{
    public User CreatedUser { get; set;}
}

As you see, it's returning an instance of what I called "Fixture". (I don't know if the name fits well).

This way, the SetupData will return an object (Fixture) with references to the entities. Thus, the test can use the created object. Otherwise, the object will be impossible to identify, since the Id isn't created until the SaveChanges is called.

My question is:

  • Is this a bad practice?
  • Is there a better way to reference initial data?

Solution

  • This is not a bad practice. In fact it is a good approach to create readable Given-When-Then tests. If you consider:

    • splitting your SetupData method
    • renaming it
    • possibly changing to a extension method
    public static MyContextExtensions
    {
        public static User Given(this MyContext @this, User user)
        {
            @this.Users.Add(user);
            @this.SaveChanges();
    
            return user;
        }
    
        public static OtherEntity Given(this MyContext @this, OtherEntity otherEntity)
        {
             // ...
        }
    
        // ...
    }
    

    you can then write (a conceptual example, details need to be reworked to match your implementation):

    [Test]
    public GivenAUser_WhenSearchingById_ReturnsTheUser()
    {
        var expectedUsername = "username";
        var user = _context.Given(AUser.WithName(expectedUsername));
    
        var result = _repository.GetUser(user.Id);
    
        Assert.That(result.Name, Is.EqualTo(expectedUsername));
    }
    

    ... and similarly for other entities.