这是我第一次编写单元测试,我只有几个问题。我正在内存数据库测试我的服务,我想知道我是否正确地进行了。我的第一个问题是我需要在所有服务电话上进行多重断言吗?像我需要InsertProduct
的断言吗?其次,我是否对此进行了测试,以便在每个服务调用上使用上下文的新实例?
[Fact]
public void ProductService_DeleteProduct_Test()
{
// arrange
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "ProductService_DeleteProduct_Test")
.Options;
var product = new Product() { Id = Guid.NewGuid(), Name = "Product"};
// act
// insert
using (var context = new ApplicationDbContext(options))
{
var service = new Service(context);
service.ProductService.InsertProduct(product);
}
// delete
using (var context = new ApplicationDbContext(options))
{
var service = new Service(context);
service.ProductService.DeleteProducts(new List<Guid> { product.Id });
}
// assert
using (var context = new ApplicationDbContext(options))
{
var service = new Service(context);
Assert.Equal(0, service.ProductService.GetAllProducts().Count);
}
}
我会反对测试的结构。也就是说,您正在使用服务(生产代码)来准备基础数据库。您还使用生产代码来做出断言。
如果生产代码的任何部分不正确,则该测试将失败。但是,该测试旨在断言删除功能做得正确。
因此,我将以以下方式重写整个测试:
[Fact]
public void ProductService_DeleteProduct_Test()
{
// arrange
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "ProductService_DeleteProduct_Test")
.Options;
var product = new Product() { Id = Guid.NewGuid(), Name = "Product"};
// Insert object using other means, i.e. direct INSERT statement
// act
using (var context = new ApplicationDbContext(options))
{
var service = new Service(context);
service.ProductService.DeleteProducts(new List<Guid> { product.Id });
}
// assert
// Execute SELECT COUNT(*) instruction to fetch previously existing row
Assert.Equal(0, rowsCount);
}
这样,您只会在测试的代理部分触摸生产代码。这就是您使用服务对象从数据库中删除对象的部分。
随后的断言是针对标量值 count
进行的,该标量值是直接在存储上执行的RAW SELECT
语句的结果。
最重要的是,测试的各个部分都不取决于生产代码的正确性,除了DeleteProducts
方法实际上是正在测试的方法。
,因此,您问题的答案是在此测试中只有一个断言。
回答您的第一个问题,我会说不。由于这是一个单元测试,并且您正在专门测试删除。我考虑设置的插入部分,因为您将系统进入要测试删除的状态。对于Zoran Horvat的观点,如果可以的话,通过服务本身以外的某些手段在数据库中放置一行。
要回答您的第二个问题,似乎没有必要使用三个块,您要在其中介绍三遍服务。我将插入,删除和断言使用,使用SUT的一个实例或服务。
但是,如果您有多个测试,这些测试都需要数据库中的一行,请考虑使用[设置]属性将每个测试都可以在手动之前调用的[设置]属性中移动到设置方法中。在这种情况下,您将使用上下文的多个实例。