我需要多个断言吗? x单元测试

Do I need multiple assert? xUnit Test

第一次写单元测试,有几个问题。我使用内存数据库来测试我的服务,我想知道我是否做对了。我的第一个问题是我的所有服务调用都需要多个断言吗?比如我需要为 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);
    }
}

回答你的第一个问题,我会说不。由于这是一个单元测试,您正在专门测试删除。我考虑设置的插入部分,因为您正在使系统进入要测试删除的状态。对于 Zoran Horvat 的观点,如果可以的话,通过服务本身以外的其他方式在数据库中添加一行。

要回答你的第二个问题,似乎没有必要在你将服务新建三次的地方使用三个块。我会将插入、删除和断言放在同一个使用中,利用 SUT 或服务的一个实例。

但是,如果您有多个测试都需要在数据库中有一行,请考虑将插入移动到具有 [SetUp] 属性的 SetUp 方法中,每个测试都可以事先调用该方法。在那种情况下,您将使用上下文的多个实例。

我会对你的测试结构提出异议。即,您正在使用服务(生产代码)来准备底层数据库。而且您还使用生产代码进行断言。

如果生产代码的任何部分不正确,则此测试将失败。但是,此测试旨在断言删除功能正常运行。

因此,我将按以下方式重写整个测试:

[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 完成的,该标量值是作为直接在存储上执行的原始 SELECT 语句的结果获取的。

底线是 none 部分测试现在取决于生产代码的正确性,DeleteProducts 方法除外,它实际上是被测方法。

因此,您的问题的答案是在这个测试中只有一个断言要写。