Unit tests in Entity Framework Core 5 - Michał Białecki Blog (2024)

Tests are an integral part of software development. These are separate programs that allow you to check if a piece of the program written by us does exactly what it should. Unit tests are small pieces of code that test individual program elements and in Entity Framework Core 5 it’s surprisingly easy to write them.

In memory or not

Microsoft recommends that when writing tests that use EF Core, you should use a real database whenever possible. In fact, it is best to use the database in exactly the same configuration and on the same server on which our application is to run. This approach may not make sense when it comes to cost, as Microsoft also admits. Performance tests should certainly check our solutions in an environment as close to production as possible. However, when writing unit tests, it’s enough to keep the database in memory. Entity Framework Core allows you to run on a virtual database created only in memory. We can also use the SQLite database because it works fast and does not need a server. It also has a mode in which it can run in memory. In this chapter, we won’t go into detail about using SQLite for testing, but I can assure you that it doesn’t take much effort.

Writing unit tests

In Entity Framework Core 5 it’s very easy to configure the database to run in memory. In a test project, just install the NuGet package called Microsoft.EntityFrameworkCore.InMemory, but also a few more might come in handy. Let’s check the full list:

  • Microsoft.EntityFrameworkCore.InMemory – to run EF Core 5 in memory
  • NUnit – a framework to write and run unit tests
  • NUnit3TestAdapter – an adapter to run NUnit tests in Visual Studio
  • FluentAssertions – easy library to write nice and readable assertions

For testing, I will use the ReservationController class. Here is its full content:

[ApiController][Route("[controller]")]public class ReservationsController : ControllerBase{ private readonly PrimeDbContext primeDbContext; public ReservationsController(PrimeDbContext _primeDbContext) { primeDbContext = _primeDbContext; } [HttpGet] public async Task<IEnumerable<Reservation>> Get() { return await primeDbContext.Reservations.Include(r => r.Room).AsNoTracking().ToListAsync(); } [HttpGet("{id}")] public async Task<IActionResult> GetById(int id) { var reservation = await primeDbContext.Reservations.FindAsync(id); if (reservation == null) { return NotFound(); } await primeDbContext.Entry(reservation).Collection(r => r.Profiles).LoadAsync(); await primeDbContext.Entry(reservation).Reference(r => r.Room).LoadAsync(); return Ok(reservation); } [HttpPost] public async Task<IActionResult> Post([FromBody] NewReservation newReservation) { var room = await primeDbContext.Rooms.FirstOrDefaultAsync(r => r.Id == newReservation.RoomId); var guests = await primeDbContext.Profiles.Where(p => newReservation.GuestIds.Contains(p.Id)).ToListAsync(); if (room == null || guests.Count != newReservation.GuestIds.Count) { return NotFound(); } var reservation = new Reservation { Created = DateTime.UtcNow, From = newReservation.From.Value, To = newReservation.To.Value, Room = room, Profiles = guests }; var createdReservation = await primeDbContext.Reservations.AddAsync(reservation); await primeDbContext.SaveChangesAsync(); return Ok(createdReservation.Entity.Id); }}

I named the test class ReservationControllerTests, which is the name of the class and the Tests ending at the end. In these tests, I will focus on checking how to replace data in Entity Framework Core, and not to test all possible cases.

The basis here is the appropriate preparation of PrimeDbContext for testing. The very base of the class with tests looks like this:

public class ReservationsControllerTests{ private DbContextOptions<PrimeDbContext> dbContextOptions = new DbContextOptionsBuilder<PrimeDbContext>() .UseInMemoryDatabase(databaseName: "PrimeDb") .Options; private ReservationsController controller; [OneTimeSetUp] public void Setup() { SeedDb(); controller = new ReservationsController(new PrimeDbContext(dbContextOptions)); } private void SeedDb() { using var context = new PrimeDbContext(dbContextOptions); var rooms = new List<Room> { new Room { Id = 1, Description = "Room nr 1", Number = 1, Level = 1, RoomType = RoomType.Standard }, new Room { Id = 2, Description = "Room nr 2", Number = 2, Level = 1, RoomType = RoomType.Standard }, new Room { Id = 3, Description = "Room nr 3", Number = 3, Level = 2, RoomType = RoomType.Suite } }; var profiles = new List<Profile> { new Profile { Id = 1, Ref = "Profile 1", Forename = "Michał", Surname = "Białecki" }, new Profile { Id = 2, Ref = "Profile 2", Forename = "John", Surname = "Show" }, new Profile { Id = 3, Ref = "Profile 3", Forename = "Daenerys", Surname = "Targaryen" } }; context.AddRange(rooms); context.AddRange(profiles); context.AddRange(new List<Reservation> { new Reservation { Id = 1, Room = rooms[0], Profiles = new List<Profile>{ profiles[0] }, From = DateTime.Today, To = DateTime.Today.AddDays(2) }, new Reservation { Id = 2, Room = rooms[2], Profiles = new List<Profile>{ profiles[1], profiles[2] }, From = DateTime.Today.AddDays(1), To = DateTime.Today.AddDays(3) } }); context.SaveChanges(); }}

The first thing that immediately catches our attention is the SeedDb method, which is used to add test data to the EF Core context. For these tests, the data will be entered only once, at the very beginning thanks to the [OneTimeSetUp] attribute. The state of the database will be preserved as long as the process that performs these tests is running. However, the more important part is at the top, which is creating a dbContextOptions. Note that this is where we use the UseInMemoryDatabase option, and then create the PrimeDbContext class using this object. When creating, we give the name of the database and always use the same one. Another very important line is:

using var context = new PrimeDbContext(dbContextOptions);

At first, we use the using keyword because we don’t want Garbage Collector to remove the context variable from memory while the test is running.

Since we already have a configured database and data, it’s time to test:

[Test]public async Task Get_FetchesReservationsWithoutRoomsAndGuests(){ using var context = new PrimeDbContext(dbContextOptions); var reservations = (await controller.Get()).ToList(); reservations.Count.Should().Be(2); reservations.All(r => r.Room == null).Should().BeFalse(); reservations.All(r => r.Profiles == null).Should().BeTrue();}

In the first test, we get all reservations and check if their dependencies are loaded. In this case, it won’t, because the Get method in the controller doesn’t force dependencies to be loaded. Let’s check another method.

[Test]public async Task GetById_WhenIdIsProvided_FetchesReservationWithRoomsAndGuests(){ using var context = new PrimeDbContext(dbContextOptions); var result = await controller.GetById(2); var okResult = result.As<OkObjectResult>(); var reservation = okResult.Value.As<Reservation>(); reservation.Should().NotBeNull(); reservation.Profiles.Should().NotBeNull(); reservation.Room.Should().NotBeNull();}

In the second test, we take a single booking and here we check that both the room and the profiles are loaded. This is because in the GetById method we use the Collection and Reference methods to load these dependencies. Now let’s test the Post method.

[Test]public async Task Post_WithRoomAndProfiles_AddsReservation(){ var newReservation = new NewReservation { From = DateTime.Today.AddDays(3), To = DateTime.Today.AddDays(7), RoomId = 3, GuestIds = new List<int> { 2 } }; using var context = new PrimeDbContext(dbContextOptions); var result = await controller.Post(newReservation); var okResult = result.As<OkObjectResult>(); var reservationId = okResult.Value.As<int>(); var addedReservation = await context.Reservations .Include(p => p.Profiles) .Include(r => r.Room) .FirstOrDefaultAsync(r => r.Id == reservationId); addedReservation.Should().NotBeNull(); addedReservation.Profiles.Should().NotBeNull(); addedReservation.Profiles.Count.Should().Be(1); addedReservation.Profiles[0].Id.Should().Be(2); addedReservation.Room.Should().NotBeNull(); addedReservation.Room.Id.Should().Be(3);}

The last test checks if the added reservation was added correctly. We check whether the room and the guest’s profile have been properly assigned to the new booking.

The summary

Unit testing in Entity Framework Core is really simple and understandable. Only a few lines of configuration allow us to use the dbContext class to prepare the desired database state. We do not have to replace individual collections in PrimeDbContext separately, as was the case with the Entity Framework tests. In this respect, Entity Framework Core is refined, and unit testing using it does not differ significantly from any other unit tests. Working with them is easy and fun, which is exactly as it should be.

All code mentioned here is available onmy GitHub, feel free to take a look at how EF Core can be used in other parts of the project.

Thanks for reading! 😊

Unit tests in Entity Framework Core 5 - Michał Białecki Blog (2024)
Top Articles
Pfannkuchen (German Pancakes) - Recipes From Europe
55 Easy Make-Ahead Thanksgiving Appetizer Recipes
My Arkansas Copa
Mr Tire Prince Frederick Md 20678
Professor Qwertyson
Santa Clara College Confidential
What happens if I deposit a bounced check?
Emmalangevin Fanhouse Leak
Craigslist Chautauqua Ny
Ssefth1203
Robert Malone é o inventor da vacina mRNA e está certo sobre vacinação de crianças #boato
Michaels W2 Online
Illinois Gun Shows 2022
Amc Flight Schedule
Echat Fr Review Pc Retailer In Qatar Prestige Pc Providers – Alpha Marine Group
Destiny 2 Salvage Activity (How to Complete, Rewards & Mission)
Dumb Money, la recensione: Paul Dano e quel film biografico sul caso GameStop
1-833-955-4522
Earl David Worden Military Service
What Is Vioc On Credit Card Statement
Outlet For The Thames Crossword
Allybearloves
Big Lots Weekly Advertisem*nt
Exl8000 Generator Battery
Www Craigslist Madison Wi
The Listings Project New York
A Man Called Otto Showtimes Near Cinemark University Mall
Rapv Springfield Ma
Sandals Travel Agent Login
How do you get noble pursuit?
The Rise of "t33n leaks": Understanding the Impact and Implications - The Digital Weekly
Ripsi Terzian Instagram
Shiftwizard Login Johnston
Gyeon Jahee
Desirulez.tv
Great Clips On Alameda
Http://N14.Ultipro.com
Rocketpult Infinite Fuel
Gets Less Antsy Crossword Clue
Comanche Or Crow Crossword Clue
War Room Pandemic Rumble
Jigidi Free Jigsaw
Actress Zazie Crossword Clue
Bank Of America Appointments Near Me
Acuity Eye Group - La Quinta Photos
antelope valley for sale "lancaster ca" - craigslist
Sitka Alaska Craigslist
The Significance Of The Haitian Revolution Was That It Weegy
Electronics coupons, offers & promotions | The Los Angeles Times
Dumb Money Showtimes Near Regal Stonecrest At Piper Glen
One Facing Life Maybe Crossword
Southern Blotting: Principle, Steps, Applications | Microbe Online
Latest Posts
Article information

Author: Gregorio Kreiger

Last Updated:

Views: 6224

Rating: 4.7 / 5 (57 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Gregorio Kreiger

Birthday: 1994-12-18

Address: 89212 Tracey Ramp, Sunside, MT 08453-0951

Phone: +9014805370218

Job: Customer Designer

Hobby: Mountain biking, Orienteering, Hiking, Sewing, Backpacking, Mushroom hunting, Backpacking

Introduction: My name is Gregorio Kreiger, I am a tender, brainy, enthusiastic, combative, agreeable, gentle, gentle person who loves writing and wants to share my knowledge and understanding with you.