如何在Entity Framework Core 2.0中模拟DbContext以进行原始查询ExecuteSqlCommand

时间:2018-10-15 18:03:39

标签: entity-framework .net-core moq

我有以下解决方案结构:

  1. MyApp.Api(“呼叫数据服务”)
  2. MyApp.Api.Test(“ Api测试”)
  3. MyApp.Data(“创建模型+迁移”)
  4. MyApp.Core(“在其中创建DbContext对象并从db获取数据的数据服务”)
  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 :(得分:0)

如果测试原始查询,那么您将需要在集成测试中针对实际数据库执行它。

使用模拟只会伪造一个响应,这将是一种误导性的测试。

配置所需的服务,以便可以对其进行测试。

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用于配置主题被测者的创建。

答案 1 :(得分:0)

尽管这有点老了,但我认为我也许可以分享我最近不得不在单元测试中模拟存储过程的经验。

就OP而言,您需要DBCheckDataService才能调用添加到DbContext.ExecuteSqlCommand的{​​{1}}。然后模拟设置将起作用。目前,您DbContext正在调用DBCheckDataService-尚未被嘲笑。

这肯定是在测试中解决模拟关系查询的一种方法。我个人不是粉丝,因为我不认为应该为您的考试修改考试科目。

可以模拟您需要在测试中包括关系查询的DbContext IF 。第二部分很重要,因为如果不需要,请使用内存提供程序。

有一些库可以模拟DbContext和相关操作的一些。我最终making my own结束了,因为没有人完成所有所有的关系操作。 EntityFrameworkCore.DbContextBackedMock.Moq模拟所有关系操作,其他所有将其传递给内存提供程序的操作。两全其美。