Как макетировать DbContext в Entity Framework Core 2.0 для необработанных запросов ExecuteSqlCommand - PullRequest
0 голосов
/ 15 октября 2018

У меня есть такая структура решения:

  1. MyApp.Api ("служба данных вызовов")
  2. MyApp.Api.Test ("Тест Api")
  3. MyApp.Data ("Создать модель + миграции")
  4. MyApp.Core ("Служба данных, в которой создается объект DbContext и извлекаются данные из базы данных")
  5. MyApp.Core.Test ("Тест службы данных ")

Мой контекст БД

namespace MyApp.Data.EF
{
    public class MyDbContext : DbContext
    {
        public MyApp(DbContextOptions<MyApp> options) : base(options){}
        public virtual int ExecuteSqlCommand(string sql, params object[] parameters)
        {
            return this.Database.ExecuteSqlCommand(sql, parameters);
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder){}
        public DbSet<User> User { get; set; }

    }
}

Сервис данных:

namespace MyApp.Core
{
    public class DBCheckDataService
    {
        private readonly DbContext _context;
        public DBCheckDataService(MetaDbContext context) {
            _context = context;
        }
        public async Task<bool> PerformHealthChecks()
        {
            var canConnect = false;
            try
            {
                var response = _context.Database.ExecuteSqlCommand("SELECT TRUE");
                return Convert.ToBoolean(Convert.ToInt16(response));
            }
            catch
            {
                return canConnect;
            }
        }
    }
}

Тестовый контроллер:

namespace MyApp.Core.Tests.Controllers
{
    [TestFixture]
    public class DBCheckDataServiceTest
    {
    public async Task checks_should_return_ok() {
        // I want to do something as below
        var mockDb = new Mock<MetaDbContext>();
        ...
        ...
        mockDb.Setup(x => x.ExecuteSqlCommand(It.IsAny<string>())).Returns(1);
        var checkDataService = new DBCheckDataServiceTest(mockDb.Object);
        var result = await checkDataService.PerformHealthChecks();
        Assert.AreEqual(false, result);
    }

    public async Task checks_should_return_false() {
        // I want to do something as below
        var mockDb = new Mock<MetaDbContext>();
        ...
        ...
        mockDb.Setup(x => x.ExecuteSqlCommand(It.IsAny<string>())).Returns(1);
        var checkDataService = new DBCheckDataServiceTest(mockDb.Object);
        var result = await DBCheckDataServiceTest.PerformHealthChecks();
        Assert.AreEqual(false, result);
    }

    }   
}

Или есть какой-то другой способ, которым я могу достичь чего-то такого же.

Ответы [ 2 ]

0 голосов
/ 13 июля 2019

Несмотря на то, что это немного устарело, я обнаружил, что, возможно, смогу поделиться своим опытом использования недавно имитированных хранимых процедур в модульных тестах.

С точки зрения ОП вам понадобитсяваш DBCheckDataService для вызова DbContext.ExecuteSqlCommand, который вы добавили к вашему DbContext.Тогда макет будет работать.В настоящее время у вас есть DBCheckDataService, вызывающий DbContext.Database.ExecuteSqlCommand - который не является поддельным.

Это, безусловно, один из способов решения ложных реляционных запросов в ваших тестах.Лично я не фанат, так как не верю, что испытуемые должны быть изменены для ваших тестов.

Можно издеваться над DbContext IF , который вам нужен для включения реляционных запросов в вашитесты.Эта вторая часть является важным битом, потому что, если вам не нужно, используйте провайдер в памяти.

Есть несколько библиотек, которые будут высмеивать DbContext и некоторые изреляционные операцииЯ закончил тем, что сделал свой собственный , поскольку не было такого, который сделал бы все реляционных операций.EntityFrameworkCore.DbContextBackedMock.Moq проверяет все реляционные операции, а все остальное направляет их поставщику в памяти.Лучшее из обоих миров.

0 голосов
/ 15 октября 2018

Если вы тестируете необработанный запрос, вам нужно будет выполнить его для реальной базы данных в интеграционном тесте.

Использование макета будет только фальсифицировать ответ, который будет вводить в заблуждение.

Сконфигурируйте желаемую службу так, чтобы ее можно было проверить.

public async Task checks_should_return_ok() {
    //Arrange
    var configuration = new ConfigurationBuilder()
        .SetBasePath("set to output path of test project")
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .Build(); //also make sure appsetting.json is copied to output.

    var serviceCollection = new ServiceCollection();

    serviceCollection.AddDbContext<MyDbContext >(options => 
        options.UseSqlServer(configuration.GetConnectionString("name here"))
    );

    serviceCollection.AddTransient<DBCheckDataService>();

    var services = serviceCollection.BuildServiceProvider();

    var subject = services.GetRequiredService<DBCheckDataService>();
    var expected = true;

    //Act
    var actual = await subject.PerformHealthChecks();

    //Assert
    Assert.AreEqual(expected, actual);        
}

ВВ приведенном выше примере строка подключения загружается из appsettings.json , а DI используется для настройки создания подчиненного объекта.

...