Entity Framework mocking требует глобального контекста - PullRequest
0 голосов
/ 26 августа 2018

Я недавно начал копаться в модульном тестировании Entity Framework с использованием насмешек Entity Framework 6.

Я заметил следующее:

Насмешка Entity Framework заставляет меня создать глобальный контекст в моем классе BL, например:

public class RefundRepayment : IDisposable
{
    protected DbContext _dbContext = new DbContext();

    /* more properties and class code */

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

Я не могуЯ понял, так как я предпочел бы реализовать оператор using в каждом методе, чтобы иметь дело с DbContext, мой код будет выглядеть так:

public class RefundRepayment
{
    /* more properties and class code */
    public void AccessDb() 
    {
        using(DbContext dbContext = new DbContext())
        {
            /* db code here */
        }
    }
}

Есть ли какая-то конкретная причина, почемумы должны инициализировать глобальный контекст вместо реализации оператора using?

1 Ответ

0 голосов
/ 27 августа 2018

Прежде всего, вам нужно использовать DI (через ninject, Unity, Core и т. Д.), Чтобы осуществить это.

Позвольте мне показать вам простой пример EF GetAll (), тестирующего мой контроллер MVC.

[Fact]
public void GetAllOk()
{
    // Arrange

    // Act
    var result = _controller.GetAll() as OkObjectResult;

    // Assert
    Assert.NotNull(result);
    var recordList = result.Value as List<DTO.Account>;
    Assert.NotNull(recordList);
    Assert.Equal(4, recordList.Count);
}

Это зависит от этого кода запуска ...

public class AccountsControllerTests
{
    DatabaseFixture _fixture;
    AccountsControllerV1 _controller;

    public AccountsControllerTests(DatabaseFixture fixture)
    {
        _fixture = fixture;
        _controller = new AccountsControllerV1(_fixture._uow);
    }

Что такое DatabaseFixture? Рад, что ты спросил ...

public class DatabaseFixture : IDisposable
{
    public ApplicationDbContext _context;
    public DbContextOptions<ApplicationDbContext> _options;
    public IUoW _uow;

    public DatabaseFixture()
    {
        var x = Directory.GetCurrentDirectory();
        var config = new ConfigurationBuilder()
            .AddJsonFile("appsettings.Tests.json", optional : true)
            .Build();

        _options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: "ProviderTests")
            .Options;

        _context = new ApplicationDbContext(_options);
        _context.Database.EnsureCreated();
        Initialize();

        _uow = new UoW(_context);
    }

    private void Initialize()
    {
        _context.Accounts.Add(new Entities.Account() { AccountNumber = "Number 1", AccountID = "", AccountUniqueID = "" });
        _context.Accounts.Add(new Entities.Account() { AccountNumber = "Number 2", AccountID = "", AccountUniqueID = "" });
        _context.Accounts.Add(new Entities.Account() { AccountNumber = "Number 3", AccountID = "", AccountUniqueID = "" });
        _context.Accounts.Add(new Entities.Account() { AccountNumber = "Number 4", AccountID = "", AccountUniqueID = "" });
        _context.SaveChanges();
    }

    public void Dispose()
    {
        // Clean Up
        _context.Database.EnsureDeleted();
    }
}

[CollectionDefinition("Database Collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{
}

Несколько определений, использованных в приведенном выше коде. Я использовал шаблон единицы работы, который содержит ссылки на все мои репозитории EF. Я держал классы Entity (База данных) и DTO (Объект Передачи Данных) отдельно. Я использовал замену в памяти для базы данных EF, которую инициализирую в начале каждого запуска и / или теста, чтобы мои данные всегда были известны . Я внедряю Fixture Database в мой тестовый класс (не каждый тест), поэтому я не создаю / уничтожаю постоянно. Затем я создаю свой контроллер, передавая в моей базе данных определение UoW.

Вы настоящий контроллер требует внедрения контейнера UoW, который вы создали с базой данных real . Вы просто заменяете контролируемую среду базы данных для своего теста.

public AccountsControllerV1(IUoW uow)
{
    _uow = uow;
}

И да, я использую версии для острых глаз. И да, это пример Core 2. Все еще применимо для EF 6, просто нужен DI стороннего производителя;)

А метод контроллера, который я тестирую?

[HttpGet("accounts", Name ="GetAccounts")]
public IActionResult GetAll()
{
    try
    {
        var recordList = _uow.Accounts.GetAll();

        List<DTO.Account> results = new List<DTO.Account>();
        if (recordList != null)
        {
            results = recordList.Select(r => Map(r)).ToList();
        }

        log.Info($"Providers: GetAccounts: Success: {results.Count} records returned");
        return Ok(results);
    }
    catch (Exception ex)
    {
        log.Error($"Providers: GetAccounts: Failed: {ex.Message}");
        return BadRequest($"Providers: GetAccounts: Failed: {ex.Message}");
    }
}
...