为什么在要测试的服务中使用我的ef核心数据上下文之前,请先对其进行处置?

时间:2018-11-12 21:58:51

标签: c# .net-core ef-core-2.0

背景:我正在围绕使用ef核心的服务编写测试。我想使用sqllite,因为它是关系型的。

我已经为测试编写了基类,该基类将使用我编写的模拟数据库工厂来设置基本的通用属性,例如http模拟和DAL。

namespace Bll.UnitTests
{
    public class TestBase : IDisposable
    {
        // pass httpclient as dependency, setup messageHandler for stubbing
        protected HttpClient httpClient;
        protected Mock<HttpMessageHandlerFake> fakeHttpMessageHandler = new Mock<HttpMessageHandlerFake> { CallBase = true };

        protected Mock<Logger> loggerMock;
        protected DalContext dataContext;

        protected MockDbFactory mockDbFactory;

        public TestBase()
        {
            mockDbFactory = new MockDbFactory();

            httpClient = new HttpClient(fakeHttpMessageHandler.Object);
            dataContext = mockDbFactory.testDb;
            loggerMock = new Mock<Logger>(dataContext);
        }

        public void Dispose()
        {
            mockDbFactory.Dispose();
        }
    }
}

这是我的模拟数据库工厂,应该在内存中建立连接并且似乎可以正常工作。

using Dal;
using Microsoft.EntityFrameworkCore;
using Moq;
using Microsoft.Data.Sqlite;
using System;
using System.Collections.Generic;

namespace Bll.UnitTests.Factories
{
    // In-memory database only exists while the connection is open
    public class MockDbFactory : IDisposable
    {
        private SqliteConnection connection;
        public DalContext testDb;

        public MockDbFactory()
        {
            OpenConnection();
            testDb = GetTestDb();
        }

        public void Dispose()
        {
            CloseConnection();
        }

        private void OpenConnection()
        {
            connection = new SqliteConnection("DataSource=:memory:");
            connection.Open();
        }

        private void CloseConnection()
        {
            connection.Close();
        }

        private DalContext GetTestDb()
        {
            var options = new DbContextOptionsBuilder<DalContext>()
                .UseSqlite(connection)
                .Options;

            // Create the schema in the database
            using (var context = new DalContext(options))
            {
                context.Database.EnsureCreated();
                return context;
            }
        }
    }
}

在我的测试类中,调试我的测试服务时会处理datacontext。

public class LocationServiceTest : TestBase
    {
        private LocationService sut;

        public LocationServiceTest(): base()
        {
            sut = new LocationService(
                httpClient,
                loggerMock.Object,
                dataContext
            );
        }

        [Fact]
        public async Task UpdateCountriesAsync_CallsCountryApiAndUpdatesDatabase()
        {
            // arrange
            // setup get country data to return 2 countries
            var temp = BuildCountryApiReturnable(2);
            fakeHttpMessageHandler.Setup(f => f.Send(It.IsAny<HttpRequestMessage>())).Returns(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.OK,
                Content = new StringContent(temp)
            });

            // act
            try
            {
                var result = await sut.UpdateCountriesAsync();

                // assert
                Assert.True(dataContext.Country.Count() == 2);
                Assert.True(dataContext.Location.Count() == 2);
            }
            catch(Exception e)
            {
                throw e;
            }

        }

我认为我理解using语句是必需的,因为这将创建我的连接并进行处理,但是我尝试手动进行操作,以便可以将数据上下文注入到我的服务中。如果必须将所有内容包装在using语句中,我将被迫更改服务。

1 个答案:

答案 0 :(得分:1)

要回答您的问题: 在MockDbFactory中,您已经通过using子句处理了上下文:

private DalContext GetTestDb()
{
    var options = new DbContextOptionsBuilder<DalContext>()
        .UseSqlite(connection)
        .Options;

    // Create the schema in the database
    using (var context = new DalContext(options))
    {
        context.Database.EnsureCreated();
        return context;  // context will already be disposed after returning
    }
}

您应该启动DalContext的新实例,并使用MockDbFactory.Dispose方法处理它的实例:

private DalContext GetTestDb()
{
    ...
    testDb = new DalContext(options);
    //Other configurations
}
...
public void Dispose()
{
    CloseConnection();
    testDb.Dispose();
}