项目

本文档有多个版本。请选择最适合您的选项。

UI
Database

Web 应用程序开发教程 - 第 4 部分:集成测试

解决方案中的测试项目

本部分介绍服务器端测试。解决方案中包含多个测试项目:

bookstore-test-projects-v2

测试项目会根据你所选的 UI 和数据库略有不同。例如,如果选择 MongoDB,那么 Acme.BookStore.EntityFrameworkCore.Tests 将变为 Acme.BookStore.MongoDB.Tests

每个项目用于测试对应的主项目。测试项目使用以下库进行测试:

使用 EphemeralMongo 库来模拟 MongoDB 数据库。每个测试都会创建并初始化一个独立的数据库实例(使用数据种子系统),以便为每个测试准备一个全新的数据库。

添加测试数据

如果你按照第一部分的描述创建了数据种子贡献者,那么测试中将可以使用相同的数据。因此,你可以跳过本节。如果尚未创建种子贡献者,你可以使用 BookStoreTestDataSeedContributor 来播种以下测试所需的数据。

测试 BookAppService

Acme.BookStore.Application.Tests 项目的 Books 命名空间(文件夹)中,添加一个名为 BookAppService_Tests 的新测试类:

using System;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Modularity;
using Volo.Abp.Validation;
using Xunit;

namespace Acme.BookStore.Books;

public abstract class BookAppService_Tests<TStartupModule> : BookStoreApplicationTestBase<TStartupModule>
    where TStartupModule : IAbpModule
{
    private readonly IBookAppService _bookAppService;

    protected BookAppService_Tests()
    {
        _bookAppService = GetRequiredService<IBookAppService>();
    }

    [Fact]
    public async Task Should_Get_List_Of_Books()
    {
        // 执行
        var result = await _bookAppService.GetListAsync(
            new PagedAndSortedResultRequestDto()
        );

        // 断言
        result.TotalCount.ShouldBeGreaterThan(0);
        result.Items.ShouldContain(b => b.Name == "1984");
    }
}

Acme.BookStore.MongoDB.Tests 项目的 MongoDb\Applications\Books 命名空间(文件夹)中,添加 BookAppService_Tests 类的一个新的实现类,名为 MongoDBBookAppService_Tests

using Acme.BookStore.MongoDB;
using Acme.BookStore.Books;
using Xunit;

namespace Acme.BookStore.MongoDb.Applications.Books;

[Collection(BookStoreTestConsts.CollectionDefinitionName)]
public class MongoDBBookAppService_Tests : BookAppService_Tests<BookStoreMongoDbTestModule>
{

}
  • Should_Get_List_Of_Books 测试简单地使用 BookAppService.GetListAsync 方法来获取并检查图书列表。
  • 我们可以安全地按名称检查图书 "1984",因为我们知道该书已存在于数据库中,因为我们在种子数据中添加了它。

BookAppService_Tests 类添加一个新的测试方法,用于创建一个新的有效图书:

[Fact]
public async Task Should_Create_A_Valid_Book()
{
    // 执行
    var result = await _bookAppService.CreateAsync(
        new CreateUpdateBookDto
        {
            Name = "New test book 42",
            Price = 10,
            PublishDate = DateTime.Now,
            Type = BookType.ScienceFiction
        }
    );

    // 断言
    result.Id.ShouldNotBe(Guid.Empty);
    result.Name.ShouldBe("New test book 42");
}

添加一个新的测试,尝试创建一个无效的图书并预期失败:

[Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
    var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
    {
        await _bookAppService.CreateAsync(
            new CreateUpdateBookDto
            {
                Name = "",
                Price = 10,
                PublishDate = DateTime.Now,
                Type = BookType.ScienceFiction
            }
        );
    });

    exception.ValidationErrors
        .ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
  • 由于 Name 为空,ABP 将抛出 AbpValidationException 异常。

最终的测试类应如下所示:

using System;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Modularity;
using Volo.Abp.Validation;
using Xunit;

namespace Acme.BookStore.Books;

public abstract class BookAppService_Tests<TStartupModule> : BookStoreApplicationTestBase<TStartupModule>
    where TStartupModule : IAbpModule
{
    private readonly IBookAppService _bookAppService;

    protected BookAppService_Tests()
    {
        _bookAppService = GetRequiredService<IBookAppService>();
    }

    [Fact]
    public async Task Should_Get_List_Of_Books()
    {
        // 执行
        var result = await _bookAppService.GetListAsync(
            new PagedAndSortedResultRequestDto()
        );

        // 断言
        result.TotalCount.ShouldBeGreaterThan(0);
        result.Items.ShouldContain(b => b.Name == "1984");
    }

    [Fact]
    public async Task Should_Create_A_Valid_Book()
    {
        // 执行
        var result = await _bookAppService.CreateAsync(
            new CreateUpdateBookDto
            {
                Name = "New test book 42",
                Price = 10,
                PublishDate = DateTime.Now,
                Type = BookType.ScienceFiction
            }
        );

        // 断言
        result.Id.ShouldNotBe(Guid.Empty);
        result.Name.ShouldBe("New test book 42");
    }
    
    [Fact]
    public async Task Should_Not_Create_A_Book_Without_Name()
    {
        var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
        {
            await _bookAppService.CreateAsync(
                new CreateUpdateBookDto
                {
                    Name = "",
                    Price = 10,
                    PublishDate = DateTime.Now,
                    Type = BookType.ScienceFiction
                }
            );
        });

        exception.ValidationErrors
            .ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
    }
}

打开 测试资源管理器窗口(如果不可见,请使用 测试 -> 窗口 -> 测试资源管理器 菜单),然后运行所有测试:

bookstore-appservice-tests

恭喜,绿色图标表示所有测试均已成功通过!


在本文档中