Entity Framework Core – Unit Testing

For easier unit testing, Entity Framework Core offers a memory-based povider. You can use the same context you use with SQL Server (or other providers) with the memory-based provider.
This article explains how you can configure Entity Framework Core to use the memory-based provider for unit testing.

Testing

This article is not about why unit testing is great, it just gives the information how to remove the dependency on a database with Entity Framework Core by using a memory-based provider – a provider that is here for unit tests.

Creating the Project To Be Tested

First, let’s create a ASP.NET Core Web application. I’m starting with the empty template and just add a few features for an API controller.

For the sample application I’m using Visual Studio 2017 RC with the .NET Core Tools MSBuild “alpha” and .NET Core 1.1. You can easily use Visual Studio 2015 Update 3 with .NET Core 1.0 as well. The features shown here are also available for Entity Framework Core 1.0.

The Book type defines the model that will be used from the EF Core context:

public class Book
{
    public int BookId { get; set; }
    public string Title { get; set; }
    public string Publisher { get; set; }
}

The BooksContext implements the EF Core context and is prepared for dependency injection by using the constructor DbContextOptions:

public class BooksContext : DbContext
{
    public BooksContext(DbContextOptions<BooksContext> options)
        : base(options) { }
    public DbSet<Book> Books { get; set; }
}

With the BooksController, the EF context is injected in the constructor, and methods such as Get make use of this context:

[Route("api/[controller]")]
public class BooksController : Controller
{
    private readonly BooksContext _booksContext;
    public BooksController(BooksContext booksContext)
    {
        _booksContext = booksContext;
    }
    // GET: api/values
    [HttpGet]
    public IEnumerable<Book> Get() => _booksContext.Books;

    // GET api/values/5
    [HttpGet("{id}")]
    public Book Get(int id) => _booksContext.Books.Find(id);

    //...

Previously I explained that the sample code can be used with Entity Framework Core 1.0 as well. Theres one exception: the Find method of the DbSet type is new with EF Core 1.1. In case you dont want to use Visual Studio 2017 yet, you can also use EF Core 1.1 with Vsiual Studio 2015 – or use the SingleOrDefault method instead of the Find method.

Lastly, the context and MVC is added to the DI container. The EF context is configured to use the SQL Server provider.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<BooksContext>(options =>      
        options.UseSqlServer(@"server=(localdb)\mssqllocaldb;database=BooksSample;trusted_connection=true")
    );
    services.AddMvc();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //...  

    app.UseMvcWithDefaultRoute();

    //...
}

The packages that need to be added to the project for using EF Core with SQL Server are

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.SqlServer

Creating the Unit Test Project

To create a unit test project, Visual Studio 2017 offers the project template xUnit Test Project (.NET Core). This project template already includes references to the Microsoft.NET.Test.Sdk and xunit NuGet packages:

  • Microsoft.NET.Test.Sdk
  • xunit
  • xunit.runner.visualstudio

To test the Web application, I’m adding a reference to the previously created Web application, and the NuGet package for the in-memory EF Core provider:

  • Microsoft.EntityFrameworkCore.InMemory
  • Microsoft.Extensions.DependencyInjection

If you use can’t use Visual Studio 2017 today, with Visual Studio 2015, to use xUnit for unit testing of .NET Core applications, you can create a Class Library (.NET Core) and add the needed NuGet packages and tools to project.json.

Let’s get into the unit testing class. In the test class the InitContext method, the BooksContext is instantiated. BooksContext needs DbContextOptions passed to the constructor. In the Web application, a DI container was created to inject the EF Context into the controller, and for use by several MVC services. Here, the BooksContext is created explicitly by creating a new instance, thus a DI container is not required for this feature. However, DbContextOptions are needed. The options can be created using an DbContextOptionsBuilder. Creating the DbContextOptionsBuilder, the EF context type is passed as a generic parameter. With this builder, the extension method UseInMemoryDatabase registers the extension InMemoryOptionsExtension to the options. This finally allows passing the configuration to the constructor of the EF context.

EF Core internally also uses a DI container where services are registered for initialization, queries, building the model, navigation, logging, and more.

After creating the EF Core context, sample data is created and added to the context. Calling the SaveChanges method, the data is permanently written to the transient store.

Enumerable.Range offers an easy way to create test data.

public class BooksControllerTest
{
    public BooksControllerTest()
    {
        InitContext();
    }

    private BooksContext _booksContext;

    public void InitContext()
    {
        var builder = new DbContextOptionsBuilder<BooksContext>()
            .UseInMemoryDatabase();

        var context = new BooksContext(builder.Options);
        var books = Enumerable.Range(1, 10)
            .Select(i => new Book { BookId = i, Title = $"Sample{i}", Publisher = "Wrox Press" });
        context.Books.AddRange(books);
        int changed = context.SaveChanges();
        _booksContext = context;
    }

    //...
}

The method that is tested is the Get method of the BooksController. The BooksController constructor receives the EF Core context that was created before. Other than that, there’s nothing special on this unit test.

[Fact]
public void TestGetBookById()
{
    string expectedTitle = "Sample2";
    var controller = new BooksController(_booksContext);
    Book result = controller.Get(2);
    Assert.Equal(expectedTitle, result.Title);
}

With this in place, you can run the unit test from the Visual Studio 2017 Test Explorer.

Test Explorer

Summary

Entity Framework Core makes it easy to create unit tests without the need to build a separate implementation of the context. The provider can be switched easily to use a memory-based provider which comes as part of EF Core, and use this one for unit tests.

Sample Code

The sample code is available at GitHub.

Have fun with programming and learning!
Christian

More Information

More information about Entity Framework Core is available in my new book and my C# workshops:

Professional C# 6 and .NET Core 1.0

Christian Nagel’s Workshops

Image from © Bradcalkins | Dreamstime.com Test Photo

One thought on “Entity Framework Core – Unit Testing

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s