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"
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.
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.
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.
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]
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With