在Service内部注入模拟的dbContext-集成测试

时间:2019-10-10 15:06:56

标签: c# .net testing asp.net-core

我正在尝试实施集成测试,因此我只能使用HttpClient测试端点

public async Task TestEndPoint()
{          
    using (var response = await _client.GetAsync($"/api/car/{id}"))
    {
        //...         
    }
}

我正在使用Fixture类来帮助我

private readonly TestServer _server;
public Fixture()
{
    var dbContextOptions = new DbContextOptionsBuilder<CarDbContext>()
                    .UseInMemoryDatabase(Guid.NewGuid().ToString())
                    .Options;

    var mockContext = new Mock<CarDbContext>(dbContextOptions);
    var mockOwnerSet = new Mock<DbSet<Owner>>();
    var mockCarSet = new Mock<DbSet<Car>>();
    mockContext.Setup(m => m.Owners).Returns(mockOwnerSet.Object);
    mockContext.Setup(m => m.Cars).Returns(mockCarSet.Object);          

    var carService = new CarService(mockContext.Object);

    _server = new TestServer(new WebHostBuilder()
        .ConfigureAppConfiguration((context, conf) =>
        {
            conf.AddJsonFile(@Directory.GetCurrentDirectory() + "../appsettings.json");
        }).UseStartup<Startup>()
            .ConfigureServices(services =>
            {
                services.AddDbContext<CarDbContext>(options => options.UseInMemoryDatabase("Test"));
                services.AddScoped<ICarService>(_ => carService);
            })
        );

    Client = _server.CreateClient();    
}   

内部Startup.cs

public class Startup
{
   public void ConfigureServices(IServiceCollection services)
   {
       services.AddDbContext<CarDbContext>(options => { options.UseSqlServer(Configuration.GetConnectionString("Db")); });
       services.AddTransient<ICarService, CarService>();
   }
}

服务类

public class CarService: ICarService {
    private readonly CarDbContext _carDbContext;

    public CarService(CarDbContext carDbContext)
    {
        _carDbContext = carDbContext;
    }

    public async Task<Car> GetAsync(int id)
    {
        return await _carDbContext.Cars.FindAsync(id); //this always returns null
    }
}

我的问题是: 为什么在CarService内未使用模拟数据库上下文

  

返回等待状态_carDbContext.Cars.FindAsync(id);   总是返回null

更新

private readonly TestServer _server;
public TestServer Server;
public Fixture()
{
    _server = new TestServer(new WebHostBuilder()
        .ConfigureAppConfiguration((context, conf) =>
        {
            conf.AddJsonFile(@Directory.GetCurrentDirectory() + "../appsettings.json");
        }).UseStartup<Startup>()
            .ConfigureServices(services =>
            {
                services.AddDbContext<CarDbContext>(options => options.UseInMemoryDatabase(Guid.NewGuid().ToString()));
            })
        );

    Client = _server.CreateClient();    
    Server = _server;
}

//内部测试方法

using (var scope = _server.Host.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<CarDbContext>();
    db.Database.EnsureDeleted();
    Utilities.Seed(db);
}

和种子数据

public static class Utilities
{
    public static void Seed(CarDbContext db)
    {
         db.Cars.Add(new Car("1", "White", "Toyota"));        
         db.SaveChanges(true);
        }
  }

我到底在哪里做错了什么? 在

中检索数据时,我仍为null
public async Task<Car> GetAsync(int id)
{
    return await _carDbContext.Cars.FindAsync(id); //this always returns null
}

1 个答案:

答案 0 :(得分:3)

您不需要模拟上下文或服务。删除_server = new TestServer行之前的所有内容。在Web主机设置中,您已经设置了上下文以使用内存中的提供程序,因此这是必需的。上下文将被注入到服务中,并且服务将被注入到需要的地方。这里唯一改变的是上下文连接。它不会像SQL Server实例那样直接运行,而只是存储和从内存中检索。

测试所要做的唯一另一件事就是将要测试的数据作为“数据库”的种子。在测试中,您需要访问测试服务器实例并执行以下操作:

using (var scope = _server.Host.Services.CreateScope())
{
    var context = scope.ServiceProvider.GetRequiredService<CarDbContext>();
    context.EnsureDeleted();
    // seed test data
}

// test code