Entity Framework Mock and Unit Test DBContext

Entity Framework Mock and Unit Test DBContext

Today in this article, we will see how to get started with the Entity Framework Mock and Unit Test DBContext class.

Today in this article, we will cover below aspects,

As we know DBContext is an autogenerated code created and customized using Scaffolding commands in Entity Framework tools.

As good practices we generally don’t write unit testing on any Scaffolded or autogenerated code. We try mocking any auto-generated code (Ex.DBCOntext) in Repository or from calling code( Ex. Constructor etc.)

So with that understanding, we shall be using the InMemoryDatabase technique to create test data and the same will be set as our DBContext to perform the Unit testing.

Here our concentration will be on how DBContext is used from either Repository or other modules.

If you are doing IntegrationTesting then please do not use the below approach as there is a difference in the scope what UnitTesting and Integration testing do.

DBContext is mostly accessed directly Ex. DBContext can be accessed directly in the Controller or can be accessed using a Repository pattern.

We shall see the sample for the same and will see how to Unit Test these instances.

Getting Started

Please create an XUnit test project and add a reference to the Service project.

To Use InMemoryDatabase please add a reference to the below Nuget package,

PM> Install-Package Microsoft.EntityFrameworkCore.InMemory -Version <version>

Define InMemory Test Database

As a first step, kindly create the DbContextOptionsBuilder option object to set up InMemoryDatabase.

Please use the below code base to initialize the InMemory Test Database,

  //create In Memory Database
            var options = new DbContextOptionsBuilder<EmployeeContext>()
            .UseInMemoryDatabase(databaseName: "EmployeeDataBase")
            .Options;

Mock and Unit Test – DBContext

Create Mocked Context Instance using the Options object created in the first step above.

  //// Create mocked Context by seeding Data as per Schema///
            using (var context = new EmployeeContext(options))
            {
                context.Employee.Add(new Employee
                {
                    EmployeeId = "12345",
                    Country = "USA",
                    State = "NY",
                    Address = "Test1",
                    ZipCode = "121312"
                });
                context.Employee.Add(new Employee
                {
                    EmployeeId = "345435",
                    Country = "UK",
                    State = "MS",
                    Address = "Test2",
                    ZipCode = "121356"
                });
                context.SaveChanges();
            }

EmployeeContext is created using scaffolding commands as discussed in our Getting Started using EFCore article.

The above setup now can be used for DBContext used in the application. A lot of developer likes to use DBCOntext directly in their Controller or through a Repository.

For both approaches, the Unit Testing technique is explained in detail below.

Unit Testing DBContext in Controller

If your DBContext instance is being used directly in the Controller using constructor injection (as shown below sample code) then please use the below code to set up the Unit Testing.

Example Code:

[Route("api/[controller]")]
    [ApiController]
    public class EmployeeDetailsController : ControllerBase
    {
        private readonly EmployeeContext _employeeContext;
        public EmployeeDetailsController(EmployeeContext context)
        {
            _employeeContext = context;
        }
        // GET: api/EmployeeDetails
        [HttpGet("{id}")]
        public async Task<ActionResult> OnGetAsync(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
            EmployeeDb db = await _employeeContext.EmployeeDb.FindAsync(id);
            if (db == null)
            {
                return NotFound();
            }
            return Ok(db);
        }

You should be able to write the Unit Test for the above code as below after following steps 1 and 2 as discussed above,

// Use a Context instance  with Data to run the test for your Business code 
           
            using (var context = new EmployeeContext(options))
            {
                EmployeeDetailsController controller = new EmployeeDetailsController(context);
                var result = await controller.OnGetAsync(345435) as ObjectResult;
                var actualResult = result.Value;
    
                Assert.Equal("12345", ((EmployeeDb)actualResult).Id);
                Assert.Equal("USA", ((EmployeeDb)actualResult).Country);
                Assert.Equal("NY", ((EmployeeDb)actualResult).State);
            }

For complete code base on using DBContext in Constructor please refer to article- Getting started with Entity Framework in ASP.NET Core.

Mock DBContext EfCore

Unit Testing DBContext with Repository

It recommended using EFCore DBContext using a Repository pattern as it encourages a more loosely coupled approach to access data from the database. The code becomes cleaner and maintainable and highly extensible.

References: Unit Test Entity Framework Core Repository

That’s all! Happy coding!

Does this help you fix your issue?

Do you have any better solutions or suggestions? Please sound off your comments below.



Please bookmark this page and share it with your friends. Please Subscribe to the blog to receive notifications on freshly published(2024) best practices and guidelines for software design and development.



4 thoughts on “Entity Framework Mock and Unit Test DBContext

  1. Hello,
    This is a good article. I have one issue though. One of my entities comes from a view, and does not have a primary key. When I mock my DbContext like, e.g.,
    context.MyView.Add(new MyView
    {
    Country = “USA”,
    State = “NY”,
    Address = “Test1”,
    ZipCode = “121312”
    });

    I get the error: “Unable to track an instance of type ‘MyView’ because it does not have a primary key. Only entity types with primary keys may be tracked.”. Could you please advise? Thank you so much

    1. Hello Ray, thanks for your query. I see your database and hence context has no primary key. EF generally needs a primary key for the operaton. You could try [Keyless] attribute on your view class.
      Example
      [Keyless]
      public class MyCustomView
      {
      public string prop1{ get; set; }
      public string prop2{ get; set; }
      }
      Let me know if it helps to resolve the issue.

Leave a Reply

Your email address will not be published. Required fields are marked *