Testing EF Core Repositories with xUnit and an In Memory Db (2024)

I came across the EF Core In Memory database recently. It obviously won’t have all the features of a relational database but it might be useful when unit testing simple repository methods.

Let’s take it for a spin …

Starting point

We have the following repository that we want to test. We’re just going to test the Add method in this post:

public interface IPersonRepository{ ... ICollection<Person> GetAll(); void Add(Person person);}
public class PersonRepository: IPersonRepository{ private PersonDataContext personDataContext; public PersonRepository(PersonDataContext personDataContext) { this.personDataContext = personDataContext; } ... public ICollection<Person> GetAll() { return personDataContext.People.ToList(); } public Person Add(Person person) { personDataContext.People.Add(person); personDataContext.SaveChanges(); return person; }}

Here’s the EF data context and models behind our repository:

public class PersonDataContext: DbContext{ public PersonDataContext() { } public PersonDataContext(DbContextOptions<PersonDataContext> options): base(options) { } public DbSet<Person> People { get; set; } public DbSet<EmailAddress> EmailAddresses { get; set; }}
public class Person{ public int PersonId { get; set; } public string Title { get; set; } public string FirstName { get; set; } public string Surname { get; set; } public ICollection<EmailAddress> EmailAddresses { get; set; }}

xUnit

So, let’s bring in xUnit by adding the following entries into

"dependencies": { ... "xunit": "2.2.0-beta5-build3474", "dotnet-test-xunit": "2.2.0-preview2-build1029"},

We also need to tell Visual Studio that xUnit is going to be our test runner by setting the following as a root property in project.json as well:

"testRunner": "xunit"

Now we can start adding xUnit tests. Let’s just add a couple of simple tests to double check xUnit is wired up properly. Let’s add the following class containing a test that should pass and a test that should fail:

public class SimpleTest{ [Fact] public void PassingTest() { Assert.Equal(2, 2); } [Fact] public void FailingTest() { Assert.Equal(2, 3); }}

If we open up Test Explorer you’ll see our tests and if you run them, 1 should pass and should 1 fail.

As a bonus, you can also run these tests from the command line by browsing to the app’s folder and running the following command:

dotnet test

Again, you should see 1 passing and 1 failing test.

In Memory Database

Before we test our repository, let’s bring in the In Memory database into our solution by adding the following dependency in

{ "dependencies": { ... "Microsoft.EntityFrameworkCore.InMemory": "1.0.1" }, ...}

Repository Tests

So, let’s start testing our Add method in our repository by creating a class and a method to test the storyline when a person has no email address:

public class PersonRepositoryTests{ [Fact] public void Add_WhenHaveNoEmail() { IPersonRepository sut = GetInMemoryPersonRepository(); Person person = new Person() { PersonId = 1, FirstName = "fred", Surname = "Blogs" }; Person savedPerson = sut.Add(person); Assert.Equal(1, sut.GetAll().Count()); Assert.Equal("fred", savedPerson.FirstName); Assert.Equal("Blogs", savedPerson.Surname); Assert.Null(savedPerson.EmailAddresses); } private IPersonRepository GetInMemoryPersonRepository() { DbContextOptions<PersonDataContext> options; var builder = new DbContextOptionsBuilder<PersonDataContext>(); builder.UseInMemoryDatabase(); options = builder.Options; PersonDataContext personDataContext = new PersonDataContext(options); personDataContext.Database.EnsureDeleted(); personDataContext.Database.EnsureCreated(); return new PersonRepository(personDataContext); }}

GetInMemoryPersonRepository is a method that all our tests will use to spin up a PersonRepository containing no data. Line 26 tells our data context to use the In Memory database. Lines 29 and 30 ensures we have a new database with no data in it.

The test is straight forward. Lines 6-12 creates a repository and a person with no email address. Line 14 calls the Add method in our repository passing in the person. Lines 16-19 carry our checks.

If you run the tests, all should be good.

Now, let’s add a couple more tests to test adding a person with single and multiple email addresses:

[Fact]public void Add_WhenHaveSingleEmail(){ IPersonRepository sut = GetInMemoryPersonRepository(); Person person = new Person() { PersonId = 1, FirstName = "fred", Surname = "Blogs", EmailAddresses = new List<EmailAddress>() { new EmailAddress() { EmailAddressId = 1, Email = "fred.blogs@testmail.com" } } }; Person savedPerson = sut.Add(person); Assert.Equal(1, sut.GetAll().Count()); Assert.Equal("fred", savedPerson.FirstName); Assert.Equal("Blogs", savedPerson.Surname); Assert.Equal(1, savedPerson.EmailAddresses.Count()); Assert.Equal("fred.blogs@testmail.com", savedPerson.EmailAddresses.ToList()[0].Email);}[Fact]public void Add_WhenHaveMultipleEmail(){ IPersonRepository sut = GetInMemoryPersonRepository(); Person person = new Person() { PersonId = 1, FirstName = "fred", Surname = "Blogs", EmailAddresses = new List<EmailAddress>() { new EmailAddress() { EmailAddressId = 1, Email = "fred.blogs@testmail.com" }, new EmailAddress() { EmailAddressId = 2, Email = "fred.blogs@anothermail.com" } } }; Person savedPerson = sut.Add(person); Assert.Equal(1, sut.GetAll().Count()); Assert.Equal("fred", savedPerson.FirstName); Assert.Equal("Blogs", savedPerson.Surname); Assert.Equal(2, savedPerson.EmailAddresses.Count()); Assert.Equal("fred.blogs@testmail.com", savedPerson.EmailAddresses.ToList()[0].Email); Assert.Equal("fred.blogs@anothermail.com", savedPerson.EmailAddresses.ToList()[1].Email);}

The tests are straightforward, following the same structure as the 1st test, using GetInMemoryPersonRepository to spin up a PersonRepository with an In Memory database behind it.

If you run the tests, all should be good.

The tests are also quick - on my machine the three tests took 7, 13 and 624 ms.

Testing EF Core Repositories with xUnit and an In Memory Db (2024)

References

Top Articles
Latest Posts
Article information

Author: Lakeisha Bayer VM

Last Updated:

Views: 5966

Rating: 4.9 / 5 (49 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Lakeisha Bayer VM

Birthday: 1997-10-17

Address: Suite 835 34136 Adrian Mountains, Floydton, UT 81036

Phone: +3571527672278

Job: Manufacturing Agent

Hobby: Skimboarding, Photography, Roller skating, Knife making, Paintball, Embroidery, Gunsmithing

Introduction: My name is Lakeisha Bayer VM, I am a brainy, kind, enchanting, healthy, lovely, clean, witty person who loves writing and wants to share my knowledge and understanding with you.