Web 应用程序开发教程 - 第 4 部分:集成测试
解决方案中的测试项目
本部分介绍服务器端测试。解决方案中包含多个测试项目:
测试项目会根据你所选的 UI 和数据库略有不同。例如,如果选择 MongoDB,那么
Acme.BookStore.EntityFrameworkCore.Tests将变为Acme.BookStore.MongoDB.Tests。
每个项目用于测试对应的主项目。测试项目使用以下库进行测试:
- Xunit 作为主要的测试框架。
- Shoudly 作为断言库。
- NSubstitute 作为模拟库。
使用 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"));
}
}
打开 测试资源管理器窗口(如果不可见,请使用 测试 -> 窗口 -> 测试资源管理器 菜单),然后运行所有测试:
恭喜,绿色图标表示所有测试均已成功通过!
抠丁客




