Testing against Entity Framework InMemory

c# entity-framework-core in-memory-database unit-testing

Question

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?
1
1
4/20/2018 2:31:20 PM

Accepted Answer

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.

1
4/20/2018 2:31:53 PM

Popular Answer

I prefer this approach:

public void SetupData(MyContext context) 
{
    var user = new User() { Id = Fixture.TEST_USER1_ID, UserName = Fixture.TEST_USER1_NAME };
    context.Users.Add(user);
    context.SaveChanges();
}

public class Fixture 
{
    public const int TEST_USER1_ID = 123;
    public const string TEST_USER!_NAME = "testuser";
}

Your approach is probably fine, too, but you probably will want to know the user ID somewhere in your tests and this makes it very easy to specify it in a single known location so that it won't change if for instance you later on change your test data and the order in which you add users.



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