Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to isolate EF InMemory database per XUnit test

I am trying use InMemory EF7 database for my xunit repository test.

But my problem is that when i try to Dispose the created context the in memory db persist. It means that one test involve other.

I have read this article Unit Testing Entity Framework 7 with the In Memory Data Store and I have tried to setup the context in the constructor of my TestClass. But this approach doesn't work. When I run tests separately everything is OK, but my first test method add something into DB and second test method start with dirty DB from previous test method. I try add IDispose into test class but method DatabaseContext and DB persist in memory. What I am doing wrong am i missing something?

My code looks like:

using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Xunit;  namespace Fabric.Tests.Repositories {     /// <summary>     /// Test for TaskRepository      /// </summary>     public class TaskRepositoryTests:IDisposable     {          private readonly DatabaseContext contextMemory;          /// <summary>         /// Constructor         /// </summary>         public TaskRepositoryTests()         {             var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>();             optionsBuilder.UseInMemoryDatabase();             contextMemory = new DatabaseContext(optionsBuilder.Options);          }          /// <summary>         /// Dispose DB          /// </summary>         public void Dispose()         {             //this has no effect              if (contextMemory != null)             {                                 contextMemory.Dispose();             }         }           /// <summary>         /// Positive Test for ListByAssigneeId method           /// </summary>         /// <returns></returns>                [Fact]         public async Task TasksRepositoryListByAssigneeId()         {             // Arrange             var assigneeId = Guid.NewGuid();             var taskList = new List<TaskItem>();               //AssigneeId != assigneeId              taskList.Add(new TaskItem()             {                 AssigneeId = Guid.NewGuid(),                 CreatorId = Guid.NewGuid(),                 Description = "Descr 2",                 Done = false,                 Id = Guid.NewGuid(),                 Location = "Some location 2",                 Title = "Some title 2"             });              taskList.Add(new TaskItem()             {                 AssigneeId = assigneeId,                 CreatorId = Guid.NewGuid(),                 Description = "Descr",                 Done = false,                 Id = Guid.NewGuid(),                 Location = "Some location",                 Title = "Some title"             });              taskList.Add(new TaskItem()             {                 AssigneeId = assigneeId,                 CreatorId = Guid.NewGuid(),                 Description = "Descr 2",                 Done = false,                 Id = Guid.NewGuid(),                 Location = "Some location 2",                 Title = "Some title 2"             });              //AssigneeId != assigneeId              taskList.Add(new TaskItem()             {                 AssigneeId = Guid.NewGuid(),                 CreatorId = Guid.NewGuid(),                 Description = "Descr 2",                 Done = false,                 Id = Guid.NewGuid(),                 Location = "Some location 2",                 Title = "Some title 2"             });               //set up inmemory DB                         contextMemory.TaskItems.AddRange(taskList);              //save context             contextMemory.SaveChanges();              // Act             var repository = new TaskRepository(contextMemory);             var result = await repository.ListByAssigneeIdAsync(assigneeId);              // Assert             Assert.NotNull(result.Count());              foreach (var td in result)             {                 Assert.Equal(assigneeId, td.AssigneeId);             }          }          /// <summary>         /// test for Add method           /// (Skip = "not able to clear DB context yet")         /// </summary>         /// <returns></returns>         [Fact]         public async Task TasksRepositoryAdd()         {             var item = new TaskData()             {                 AssigneeId = Guid.NewGuid(),                 CreatorId = Guid.NewGuid(),                 Description = "Descr",                 Done = false,                 Location = "Location",                 Title = "Title"             };               // Act             var repository = new TaskRepository(contextMemory);             var result = await repository.Add(item);              // Assert             Assert.Equal(1, contextMemory.TaskItems.Count());             Assert.NotNull(result.Id);              var dbRes = contextMemory.TaskItems.Where(s => s.Id == result.Id).SingleOrDefault();             Assert.NotNull(dbRes);             Assert.Equal(result.Id, dbRes.Id);         }       } } 

I am using:

"Microsoft.EntityFrameworkCore.InMemory": "1.0.0"  "Microsoft.EntityFrameworkCore": "1.0.0"  "xunit": "2.2.0-beta2-build3300" 
like image 304
Ivan Mjartan Avatar asked Aug 11 '16 07:08

Ivan Mjartan


People also ask

How do I mock database connection in xUnit?

You should adhere to Interface Segregation and Dependency Inversion principle by using Inversion of Control with the help of Dependency Injection. This way, you can create a MockDB2Connection, which you inject into the constructor, in your unit tests, while in your real code, you pass a proper DB2Connection.

Does xUnit create a new class for each test?

xUnit.net creates a new instance of the test class for every test that is run, so any code which is placed into the constructor of the test class will be run for every single test.

How do I ignore test cases in xUnit?

xUnit.net does not require an attribute for a test class; it looks for all test methods in all public (exported) classes in the assembly. Set the Skip parameter on the [Fact] attribute to temporarily skip a test.

Which of the following attributes are eliminated in xUnit?

Here is the list of attributes removed from the framework: [Setup] and [TearDown] are replaced with Constructors & IDisposable. [TestFixture] is removed. [Ignore] is absorbed with Skip = parameter on [Fact]


1 Answers

From the documentation,

Typically, EF creates a single IServiceProvider for all contexts of a given type in an AppDomain - meaning all context instances share the same InMemory database instance. By allowing one to be passed in, you can control the scope of the InMemory database.

Instead of making the test class disposable and trying to dispose the data context that way, create a new one for each test:

private static DbContextOptions<BloggingContext> CreateNewContextOptions() {     // Create a fresh service provider, and therefore a fresh      // InMemory database instance.     var serviceProvider = new ServiceCollection()         .AddEntityFrameworkInMemoryDatabase()         .BuildServiceProvider();      // Create a new options instance telling the context to use an     // InMemory database and the new service provider.     var builder = new DbContextOptionsBuilder<DatabaseContext>();     builder.UseInMemoryDatabase()            .UseInternalServiceProvider(serviceProvider);      return builder.Options; } 

Then, in each test, new up a data context using this method:

using (var context = new DatabaseContext(CreateNewContextOptions())) {     // Do all of your data access and assertions in here } 

This approach should get you a squeaky-clean in-memory database for each test.

like image 186
Nate Barbettini Avatar answered Sep 24 '22 21:09

Nate Barbettini