如何在每个 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.
我已阅读这篇文章 使用内存中数据存储对实体框架 7 进行单元测试,我尝试在我的 TestClass 的构造函数中设置上下文.但是这种方法行不通.当我单独运行测试时,一切正常,但我的第一个测试方法将一些内容添加到数据库中,第二个测试方法从先前测试方法中的脏数据库开始.我尝试将 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 为 AppDomain 中给定类型的所有上下文创建单个 IServiceProvider
- 这意味着所有上下文实例共享相同的 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屋!