如何测试具有 Db 上下文和记录器的 Asp.Net 核心控制器?
How do I test the Asp.Net Core controller which has Db Context and Logger?
我有一个带有两个参数的控制器,需要通过单元测试来测试它们。想要测试 4 个参数、ViewBug 等。但是如何制作假数据库上下文和记录器?我现在卡住了:
[Fact]
public void IndexReturnsAViewResultWithAListOfUsers()
{
// Arrange
var mock = new Mock<AircraftsController>();
var controller = new AircraftsController(/*params*/);
// Act
// Assert
}
这是我的控制器:
public class AircraftsController : Controller
{
#region DbContext, Logger
public AppDbContext Context { get; }
private readonly ILogger<AircraftsController> _logger;
public AircraftsController(AppDbContext context, ILogger<AircraftsController> logger)
{
Context = context;
_logger = logger;
_logger.LogDebug(1, "NLog injected into Controller");
}
#endregion
[HttpGet]
public IActionResult Compare(int vehicle1, int vehicle2, int vehicle3, int vehicle4)
{
var planesFromDb = Context.Planes.OrderBy(x => x.BR).ToList();
planesFromDb.Insert(0, new Plane { Image = "~/images/EmptyPlane.png", Nation = "EmptyFlag", Name = "Select aircraft", VehicleId=0 });
var selectedPlanes = new List<Plane>();
ViewBag.AllPlanesSelected = planesFromDb;
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle1));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle2));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle3));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle4));
_logger.LogInformation("Log Message");
return View(selectedPlanes);
}
}
在 .NET Core 中,您可以利用内存数据库进行单元测试。有两个选项,EF In-Memory 数据库和 SQLite In-Memory 数据库。我更喜欢 SQLite In-Memory,因为它为您提供了处理关系数据的所有优势,这与 EF In-Memory 不同。
Testing with the EF In-Memory Database
下面是使用 SQLite In-Memory 数据库进行单元测试的简单实现:
public YourContext GetDbContext()
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var option = new DbContextOptionsBuilder<YourContext>()
.UseSqlite(connection,
s => {
s.UseNetTopologySuite();
s.MigrationsHistoryTable("__MigrationHistory");
}).Options;
var dbContext = new YourContext(option);
//Added to recreate database and run migration for each test.
if (dbContext != null)
{
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
}
return dbContext;
}
然后在单元测试中:
var context = GetDbContext();
或者,您可以将 GetDbContext 方法放在固定装置中,以便每次测试只重新创建一次数据库 class。在 运行 dbContext.Database.EnsureDeleted() 的 fixture 中有一个 dispose 方法来清理测试 classes.
之间的数据
正如 Stephen 所建议的那样,内存中提供程序是模拟 EFCore 数据库上下文的不错选择。它适用于大多数情况。
我需要将 Moq 用于第 3 方依赖项,因此我会为两者创建模拟。对于数据库上下文,我将使用 EntityFrameworkCore.Testing(免责声明,我是作者):
var mockedDbContext = Create.MockedDbContextFor<AppDbContext>();
然后对于记录器,我将使用 Mock.Of
创建一个模拟
var mockedLogger = Mock.Of<ILogger<AircraftsController>>();
简单的一行,然后您可以使用它在单元测试中创建控制器。总的来说,如果适合单元测试,我是使用 EFCore 内存中提供程序的提倡者。使用模拟确实有其他优势,例如允许您验证调用。
我有一个带有两个参数的控制器,需要通过单元测试来测试它们。想要测试 4 个参数、ViewBug 等。但是如何制作假数据库上下文和记录器?我现在卡住了:
[Fact]
public void IndexReturnsAViewResultWithAListOfUsers()
{
// Arrange
var mock = new Mock<AircraftsController>();
var controller = new AircraftsController(/*params*/);
// Act
// Assert
}
这是我的控制器:
public class AircraftsController : Controller
{
#region DbContext, Logger
public AppDbContext Context { get; }
private readonly ILogger<AircraftsController> _logger;
public AircraftsController(AppDbContext context, ILogger<AircraftsController> logger)
{
Context = context;
_logger = logger;
_logger.LogDebug(1, "NLog injected into Controller");
}
#endregion
[HttpGet]
public IActionResult Compare(int vehicle1, int vehicle2, int vehicle3, int vehicle4)
{
var planesFromDb = Context.Planes.OrderBy(x => x.BR).ToList();
planesFromDb.Insert(0, new Plane { Image = "~/images/EmptyPlane.png", Nation = "EmptyFlag", Name = "Select aircraft", VehicleId=0 });
var selectedPlanes = new List<Plane>();
ViewBag.AllPlanesSelected = planesFromDb;
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle1));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle2));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle3));
selectedPlanes.Add(planesFromDb.FirstOrDefault(p => p.VehicleId == vehicle4));
_logger.LogInformation("Log Message");
return View(selectedPlanes);
}
}
在 .NET Core 中,您可以利用内存数据库进行单元测试。有两个选项,EF In-Memory 数据库和 SQLite In-Memory 数据库。我更喜欢 SQLite In-Memory,因为它为您提供了处理关系数据的所有优势,这与 EF In-Memory 不同。
Testing with the EF In-Memory Database
下面是使用 SQLite In-Memory 数据库进行单元测试的简单实现:
public YourContext GetDbContext()
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var option = new DbContextOptionsBuilder<YourContext>()
.UseSqlite(connection,
s => {
s.UseNetTopologySuite();
s.MigrationsHistoryTable("__MigrationHistory");
}).Options;
var dbContext = new YourContext(option);
//Added to recreate database and run migration for each test.
if (dbContext != null)
{
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
}
return dbContext;
}
然后在单元测试中:
var context = GetDbContext();
或者,您可以将 GetDbContext 方法放在固定装置中,以便每次测试只重新创建一次数据库 class。在 运行 dbContext.Database.EnsureDeleted() 的 fixture 中有一个 dispose 方法来清理测试 classes.
之间的数据正如 Stephen 所建议的那样,内存中提供程序是模拟 EFCore 数据库上下文的不错选择。它适用于大多数情况。
我需要将 Moq 用于第 3 方依赖项,因此我会为两者创建模拟。对于数据库上下文,我将使用 EntityFrameworkCore.Testing(免责声明,我是作者):
var mockedDbContext = Create.MockedDbContextFor<AppDbContext>();
然后对于记录器,我将使用 Mock.Of
var mockedLogger = Mock.Of<ILogger<AircraftsController>>();
简单的一行,然后您可以使用它在单元测试中创建控制器。总的来说,如果适合单元测试,我是使用 EFCore 内存中提供程序的提倡者。使用模拟确实有其他优势,例如允许您验证调用。