Util应用框架快速入门(4) - 集成测试开发入门
本文演示Util应用框架开发的项目中如何编写集成测试.
准备
完成 Web Api 快速入门,本文将在之前生成的示例项目上讲解集成测试的开发.
测试概述
自动化测试对于Util应用框架的开发非常重要,它能保证基础功能的稳定性.
对于使用 Util 开发的业务项目,自动化测试不是必须的,但掌握它可能很有用.
如果你使用 Util 开发 Web API,可能会使用 Swagger 进行测试.
将 Swagger 提供给前端人员是合适的,但后端人员使用它却不够省力.
原因很简单,使用 Swagger 测试 API,需要设置一堆参数,这些参数无法保存,每次运行都需要设置.
使用 .Net 自动化测试会更加方便,并且现在开发集成测试的成本很低.
专业测试的分类非常细,下面简要讨论自动化功能测试.
测试分类
这里粗略的对自动化功能测试概括为两类:
-
单元测试
- 隔绝外部依赖,仅测试自身的某些功能.
- 如果需要访问外部依赖,通过定义抽象接口的方式使用,不能直接引用.
- 在 Util 分层架构中,一般对领域层实体,值对象,领域服务实施单元测试.
-
集成测试
- 直接访问外部依赖,对关联的所有类型进行测试.
- 在 Util 分层架构中,一般对基础设施层仓储,应用层应用服务和API接口进行集成测试.
测试的好处
修改任何一行业务代码,都有可能影响之前的逻辑.
自动化测试基于对业务功能的预期反应,如果预期未发生变化,但你的代码逻辑出现变化,就能帮你拦截这个错误.
测试的弊端
-
除了开发自动化测试本身的成本以外,更大的成本在于维护.
-
每当需求变更,需要删除已经过时的测试,开发新的测试,修改之前的测试以符合当前预期.
测试编写条件
自动化测试的语法非常简单,但并不是掌握了测试语法就能编写出有效的测试.
编写单元测试需要开发人员有一定抽象思维,能够抽象和隔离依赖,还需要了解一些单元测试技巧.
不要在公司全面推行单元测试,容易变成形式主义,仅让团队核心骨干对高价值业务模块编写单元测试.
集成测试则要简单得多,只要懂得测试语法就可以编写.
集成测试由于直接访问外部依赖,运行缓慢,而且任何环节变更都可能导致测试失败,所以不宜大量编写.
你是否需要它?
-
如果你仅负责编写 Web API ,手头没有现成的UI进行测试,编写集成测试比使用 Swagger 省力.
-
如果你没有打算持续维护这些测试,不要编写它们,那只会浪费时间.
-
如果你的团队精力有限,无法维护大量的测试,可以仅对高业务价值的模块编写测试.
测试框架选型
-
测试框架
- XUnit.Net - 提供基础测试语法.
- XUnit.DependencyInjection - 为测试框架提供依赖注入能力.
-
模拟框架
- Moq - 为依赖服务接口创建模拟实现.
-
数据伪造框架
Util集成测试开发入门
单元测试用于测试复杂的业务逻辑,由于快速入门Demo仅包含简单CRUD操作,无法演示单元测试的用法.
下面介绍在Util项目中如何开发集成测试.
运行示例项目
打开示例项目解决方案 Demo.sln,查看 test 子目录,它包含4个测试项目.
生成的测试项目已经将环境配置完成,你可以直接开始编写测试.
Demo.Domain.Tests 是领域层的单元测试项目,如果你不需要编写单元测试,直接删掉它.
其它三个项目用于集成测试,下面分别介绍.
数据访问层集成测试
Demo.Data.SqlServer.Tests 是数据访问层 Sql Server 集成测试项目.
如果支持多种数据库,则每种数据库应包含一个数据访问层集成测试项目.
数据访问层测试的重点是仓储.
打开 StudentRepositoryTest 学生仓储测试类.
在 StudentRepositoryTest 的构造方法中注入了依赖接口 IDemoUnitOfWork 和 IStudentRepository.
Util 代码生成模板默认会创建 TestAddAsync 方法,它用于测试添加实体.
该测试使用伪数据生成框架创建学生实体,并通过仓储保存到数据库.
不宜编写过多的CRUD集成测试,通常保留一两个即可.
它们的作用不是测试逻辑,而是迅速识别开发环境是否通畅.
特别是当你升级框架或类库时,有几个简单的集成测试非常有用.
运行数据访问层集成测试
打开 appsettings.Development.json 配置文件,检查连接字符串是否正确.
{
"ConnectionStrings": {
"connection": "Server=.;Database=Demo.Data.Test;uid=sa;pwd=Pass@word;TrustServerCertificate=true"
}
}
Demo示例的数据访问层测试数据库名为 Demo.Data.Test ,运行测试时, EntityFrameworkCore 会自动创建和删除测试数据库,非常方便.
使用 Visual Studio 运行测试
右键单击 TestAddAsync 方法,弹出菜单选择 运行测试.
弹出 测试资源管理器 窗口,并自动运行测试.
使用 Resharper 运行测试
如果你安装了Resharper, VS中的测试方法会在左侧显示测试图标,如下图所示.
点击 Run 按钮,运行测试.
应用层集成测试
应用层集成测试包含 Demo.Application.Tests 和 Demo.Api.Tests 两个测试项目.
应用服务集成测试
Demo.Application.Tests 侧重于测试应用服务,并且没有Asp.Net Core相关环境干扰.
对于普通项目,大多业务逻辑会写到应用服务中, 所以它是比较理想的测试场所.
如果你不想维护太多测试项目,那么仅保留应用服务集成测试即可.
打开 StudentServiceTest 测试类,代码生成模板默认也创建了一个 TestCreateAsync 测试方法.
/// <summary>
/// 学生服务测试
/// </summary>
public class StudentServiceTest {
/// <summary>
/// 学生服务
/// </summary>
private readonly IStudentService _service;
/// <summary>
/// 测试初始化
/// </summary>
public StudentServiceTest( IStudentService service ) {
_service = service;
}
/// <summary>
/// 测试创建
/// </summary>
[Fact]
public async Task TestCreateAsync() {
//创建
var dto = StudentDtoFakeService.GetStudentDto();
var id = await _service.CreateAsync( dto );
//验证
var result = await _service.GetByIdAsync( id );
Assert.NotNull( result );
Assert.Equal( id, result.Id );
}
}
StudentServiceTest 从构造方法注入了 IStudentService 应用服务接口.
使用伪数据生成框架创建DTO,并调用服务保存数据.
Web Api集成测试
Demo.Api.Tests 用于测试 Web Api控制器,它的测试环境要复杂一些.
打开 StudentControllerTest 测试类.
/// <summary>
/// 学生控制器测试
/// </summary>
public class StudentControllerTest : TestBase {
/// <summary>
/// 输出工具
/// </summary>
private readonly ITestOutputHelper _testOutputHelper;
/// <summary>
/// 测试初始化
/// </summary>
public StudentControllerTest( ITestOutputHelperAccessor testOutputHelperAccessor,IHttpClient client ) : base( client ){
_testOutputHelper = testOutputHelperAccessor.Output;
}
/// <summary>
/// 测试创建
/// </summary>
[Fact]
public async Task TestCreateAsync() {
//服务地址
var url = "/api/student";
//创建实体
var dto = StudentDtoFakeService.GetStudentDto();
var result = await PostAsync<StudentDto>( url, dto );
//验证
Assert.Equal( StateCode.Ok, result.Code );
Assert.NotEmpty( result.Data.Id );
_testOutputHelper.WriteLine( Json.ToJson( result ) );
}
}
Web Api的测试需要发送请求给控制器,并接收响应结果,所以需要一个Http客户端.
StudentControllerTest 构造方法注入 Util Http客户端接口,将接口传递给 TestBase 基类.
TestBase 进一步封装对 GET, POST ,PUT ,DELETE 操作请求,以简化测试的编写.
默认生成的 TestCreateAsync 方法,使用伪数据生成框架创建DTO, 并使用 Post 请求指定 Url.
Web Api 控制器返回 Util 约定的特定消息.
对于开发 Web Api, 该集成测试能更好的反映与前端的交互.
运行 Web Api集成测试,如下所示.