如何通过XUnit测试隔离EF InMemory数据库 [英] How to isolate EF InMemory database per XUnit test

查看:120
本文介绍了如何通过XUnit测试隔离EF InMemory数据库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆