第 9 章 测试和文档
9.1 测试
测试是软件生命周期中的一个非常重要的阶段,对于保证软件的可靠性具有极其重要的意义
常见的测试方法有很多,根据不同的维度,可以把测试方法分为不同的类别
从观察结构的透明性方式上,分为白盒测试、黑盒测试和灰盒测试
从测试执行方式上,分为手动测试和自动化测试
从测试所涉及的层次上,分为单元测试、集成测试和系统测试
单元测试由开发人员完成,主要用来测试程序中的类以及其中的方法是否能够正确运行
在添加单元测试方法时,应遵循 Arrange-Act-Access 模式,使测试方法的代码更加规范,该模式指明了每个测试方法由以下3部分组成:
- Arrange:为测试进行准备操作,如设置测试数据、变量和环境等
- Act:执行要测的方法,如调用要测试的函数和方法
- Assert:断言测试结果,验证被测试方法的输出是否与预期的结果一致
我们使用 xUnit 测试框架,为项目命名为 Library.API.Testing
项目创建完成后,将默认的 UnitTest1.cs 删除,并添加一个新类,名为 AuthorController_UnitTests,该类主要包含针对 AuthorController 中方法的测试方法
由于 AuthorController 的构造函数中引用了多个其他依赖项,如 IRepositoryWrapper 和 IMapper 等,可以使用 Moq 库来模拟
代码语言:javascript复制namespace Library.API.Testing
{
public class AuthorController_UnitTests
{
private AuthorController _authorController;
private Mock<IDistributedCache> _mockDistributedCache;
private Mock<ILogger<AuthorController>> _mockLogger;
private Mock<IMapper> _mockMapper;
private Mock<IRepositoryWrapper> _mockRepositoryWrapper;
private Mock<IUrlHelper> _mockUrlHelper;
public AuthorController_UnitTests()
{
_mockRepositoryWrapper = new Mock<IRepositoryWrapper>();
_mockMapper = new Mock<IMapper>();
_mockLogger = new Mock<ILogger<AuthorController>>();
_mockDistributedCache = new Mock<IDistributedCache>();
_mockUrlHelper = new Mock<IUrlHelper>();
_authorController = new AuthorController(_mockMapper.Object,
_mockRepositoryWrapper.Object,
_mockLogger.Object,
_mockDistributedCache.Object);
_authorController.ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext()
};
}
}
}
在 AuthorController 中用到了 Response 属性,该属性可以控制 HTTP 响应,如在 GetAuthorsAsync 方法中使用它为响应添加了自定义消息头
代码语言:javascript复制Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(paginationMetedata));
已实例化的 AuthorController 的 Response 属性默认为空,通过设置 ControllerContext,使得 AuthorController 对象的 Request 和 Response 属性将不再为空
接下来对 GetAuthorsAsync 添加测试方法
代码语言:javascript复制[Fact]
public async Task Test_GetAuthorsAsync()
{
// Arrange
var author = new Author
{
Id = Guid.NewGuid(),
Name = "Author Test 1",
Email = "author1@xxx.com"
};
var authorDto = new AuthorDto
{
Id = author.Id,
Name = author.Name,
Email = author.Email,
};
var authorList = new List<Author> {author};
var authorDtoList = new List<AuthorDto> {authorDto};
var parameters = new AuthorResourceParameters();
var authors = new PagedList<Author>(authorList, totalCount: authorList.Count,
pageNumber: parameters.PageNumber, pageSize: parameters.PageSize);
_mockRepositoryWrapper.Setup(m => m.Author.GetAllAsync(It.IsAny<AuthorResourceParameters>()))
.Returns(Task.FromResult(authors));
_mockMapper.Setup(m => m.Map<IEnumerable<AuthorDto>>(It.IsAny<IEnumerable<Author>>()))
.Returns(authorDtoList);
_mockUrlHelper.Setup(m => m.Link(It.IsAny<string>(), It.IsAny<object>())).Returns("demo url");
_authorController.Url = _mockUrlHelper.Object;
// Act
var actionResult = await _authorController.GetAuthorsAsync(parameters);
// Assert
List<AuthorDto> resourceCollection = actionResult.Value;
Assert.True(1==resourceCollection.Count);
Assert.Equal(authorDto,resourceCollection.First());
Assert.True(_authorController.Response.Headers.ContainsKey("X-Pagination"));
}
上面的测试方法遵循了 Arrange-Act-Assert 的模式
当数据准备好后,调用待测试的方法,并得到该方法的返回值,之后使用 Assert 类提供的静态方法来验证结果是否符合预期