Почему мой основной контекст данных ef удаляется перед использованием в тестируемой службе?

Предыстория: я пишу тесты для сервисов, использующих ef core. Я хочу использовать sqllite, поскольку он реляционный.

Я написал базовый класс для тестов, которые будут использовать фиктивную фабрику баз данных, которую я написал для настройки основных общих вещей, таких как http mocking и 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, я буду вынужден изменить свой сервис.


person Craig    schedule 12.11.2018    source источник
comment
Entity Framework Core предоставляет базу данных в памяти именно для тестирования. Не пытайтесь имитировать EF Core, используйте его по назначению   -  person Camilo Terevinto    schedule 13.11.2018
comment
@CamiloTerevinto, OP не издевается над EF Core, вместо этого OP использует поставщика SQLLite с параметром в памяти, который аналогичен EF Core в базе данных в памяти, но с ограничениями базы данных отношения.   -  person Fabio    schedule 13.11.2018
comment
Удалите using в методе GetTestDb и сделайте класс потребителя ответственным за удаление созданного контекста. Если да, то CreateTestDb было бы лучшим именем.   -  person Fabio    schedule 13.11.2018


Ответы (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();
}
person Lowkey    schedule 12.11.2018
comment
Я иногда такой тупой. Спасибо большое - person Craig; 13.11.2018