Click here to Skip to main content
15,881,655 members
Articles / Web Development / ASP.NET

Testing Entity Framework Core

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
12 Nov 2018CPOL4 min read 4.8K   2  
How to write automated tests using entity framework core

Introduction

In this post, we are going to have a look at how we can write automated tests using entity framework core.

As in the recent previous posts, the code can be found here.

The Story

A while back when working on a project, I wanted to do an integration test with a mock database so that I can validate that the database constraints are actually working and are configured properly.

So in my search to find a viable solution that will do the job but also won’t carry a lot of overhead in both processing power and cleanup, I ended up on this documentation page.

If you took a look at the documentation page, you will see that the documentation actually specifies two approaches for this.
So after having a look over both of them, I thought the InMemory approach would be better for me since I didn’t want to include an additional dependency to SQLite.
Well, soon after I followed the example and implemented it, I found out that it wasn’t in fact what I needed, mostly because of two major reasons:

  • The InMemory database requires a name. The reason for this so that you can use the same database across tests. This was mostly an annoyance for me since I didn’t want to recreate the database in every test, and if I can help not repeating myself, all the better.
  • The InMemory database is not a relational database, it’s not really much of a database at all since I found out the hard way that all the constraints I configured into my database context weren’t even validated. If I wasn’t following the Test Driven Development approach, I would have found out about a bug in my code when manually testing or worse, in production.

This brings me to this post’s topic, doing in memory testing using SQLite, reusing the functionality, and even inspecting the generated queries.

The Setup

First off, we will create a new ASP.NET Core MVC project with individual authentication (this is mostly because it comes already configured with a database context, of course, you can roll your own).

Next step, and where we will do most of the work, is the test project. For that, we will need a .NET Core Console project.

Once we have the test project and have referenced our WebApplication project (so we can reach the DbContext), we will need to install some Nuget packages:

  • Microsoft.NET.Test.Sdk (15.7.2)
    • This is needed to actually run the units tests.
  • NUnit (3.10.1)
    • My testing framework of choice, though you can use any framework that suits your needs.
  • NUnit3TestAdapter (3.10)
    • This is so that ReSharper and Visual Studio can find and run the tests.
  • Microsoft.AspNetCore.App
    • Installing this package since we’re making use of the IdentityDbContext, though this is only because of the template I opted to use for this example.
  • Microsoft.EntityFrameworkCore.Sqlite (2.1.0)

The Implementation

Now that we have all that we need, let’s move on to the implementation.

Out of habit, inside the test project, I usually create a folder called TestUtilities and inside that, a static class called TestDatabaseContextFactory.

I’ve annotated the class line by line to explain what everything does.

C#
namespace Test.TestUtilities
{
    using Microsoft.Data.Sqlite;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Logging;

    using WebApplication.Data;

    public static class TestDatabaseContextFactory
    {
        public static ApplicationDbContext CreateDbContext()
        {
            DbContextOptionsBuilder<ApplicationDbContext> 
                    contextOptionsBuilder = // declaring the options we are going to use 
                                            // for the context we will be using for tests
                new DbContextOptionsBuilder<ApplicationDbContext>();

            LoggerFactory loggerFactory = new LoggerFactory(); // this will allow us to add
             // loggers so we can actually inspect what code and queries EntityFramework produces.
            loggerFactory
                .AddDebug() // this logger will log to the Debug output
                .AddConsole(); // this logger will output to the console

            SqliteConnectionStringBuilder connectionStringBuilder = 
              new SqliteConnectionStringBuilder { Mode = SqliteOpenMode.Memory }; // this is more 
                  // syntax friendly approach to defining and InMemory connection string for our 
                  // database, the alternative is to write it out as a string.
            SqliteConnection connection = 
                new SqliteConnection(connectionStringBuilder.ConnectionString); // create a 
                                                        // connection to the InMemory database.
            connection.Open(); // open the connection

            contextOptionsBuilder.UseLoggerFactory(loggerFactory); // register the loggers 
                 // inside the context options builder, this way, entity framework logs the queries
            contextOptionsBuilder.UseSqlite(connection); // we're telling entity framework 
                                                         // to use the SQLite connection we created.
            contextOptionsBuilder.EnableSensitiveDataLogging(); // this will give us more 
                 // insight when something does go wrong. It's ok to use it here since it's a 
                 // testing project, but be careful about enabling this in production.

            ApplicationDbContext context = 
            new ApplicationDbContext(contextOptionsBuilder.Options); // creating the actual DbContext

            context.Database.EnsureCreated(); // this command will create the schema 
              // and apply configurations we have made in the context, like relations and constraints

            return context; // return the context to be further used in tests.
        }
    }
}

With this in place, we can now actually use it in our tests like so:

C#
namespace Test
{
    using NUnit.Framework;

    using Test.TestUtilities;

    using WebApplication.Data;

    [TestFixture]
    public class TestingDatabaseCreation
    {
        [Test]
        public void TestCreation()
        {
            ApplicationDbContext context = TestDatabaseContextFactory.CreateDbContext();
        }
    }
}

Even though this test doesn’t assert anything, it will still pass, and with the added bonus that if we look in the output of the test run, we will see all the queries that have been run against the database.

Conclusion

This might not seem much, but it has become a standard to use it in my projects because even though the idea of unit tests is nice, there are times when our tests need to be small and fast but still do actual application logic, which most of the times especially when using SQL, the functionality doesn’t only live inside the application.

Keep in mind that this approach is using SQLite, so if you’re using some other database or a NoSQL database, look into their in memory variants if they have them. That being said, SQLite and SQL Server are different as well, but 9 out 10 functionalities that we will be using Entity Framework for is present in both.

I hope you enjoyed this post, and there are a few others related to Entity Framework Core on the horizon.

Cheers!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Romania Romania
When asked, I always see myself as a .Net Developer because of my affinity for the Microsoft platform, though I do pride myself by constantly learning new languages, paradigms, methodologies, and topics. I try to learn as much as I can from a wide breadth of topics from automation to mobile platforms, from gaming technologies to application security.

If there is one thing I wish to impart, that that is this "Always respect your craft, your tests and your QA"

Comments and Discussions

 
-- There are no messages in this forum --