如何通过XUnit测试隔离EF InMemory数据库 [英] How to isolate EF InMemory database per XUnit test
问题描述
我正在尝试将InMemory EF7数据库用于我的xunit存储库测试。
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.
我已阅读这篇文章使用内存数据存储进行单元测试Entity Framework 7 ,我试图在TestClass的构造函数中设置上下文。但是这种方法行不通。当我分别运行测试时,一切正常,但是我的第一个测试方法向DB中添加了一些内容,第二个测试方法从先前测试方法中的脏DB开始。我尝试将 IDispose
添加到测试类中,但是方法DatabaseContext和DB保留在内存中。我做错了我错过了什么吗?
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?
我的代码如下:
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);
}
}
}
我正在使用:
"Microsoft.EntityFrameworkCore.InMemory": "1.0.0"
"Microsoft.EntityFrameworkCore": "1.0.0"
"xunit": "2.2.0-beta2-build3300"
推荐答案
来自文档,
通常,EF创建单个
IServiceProvider
用于AppDomain中给定类型的所有上下文-意味着所有上下文实例共享相同的InMemory数据库实例。通过允许传递一个,您可以控制InMemory数据库的范围。
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.
这篇关于如何通过XUnit测试隔离EF InMemory数据库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!