第十二章 测试和调试
12.1 单元测试
设置测试项目
# 创建测试项目
dotnet new xunit -n TodoApi.Tests
# 添加引用
cd TodoApi.Tests
dotnet add reference ../TodoApi/TodoApi.csproj
# 添加测试包
dotnet add package Microsoft.AspNetCore.Mvc.Testing
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Moq
控制器单元测试
public class TodoItemsControllerTests
{
private readonly Mock<ITodoRepository> _mockRepository;
private readonly Mock<ILogger<TodoItemsController>> _mockLogger;
private readonly TodoItemsController _controller;
public TodoItemsControllerTests()
{
_mockRepository = new Mock<ITodoRepository>();
_mockLogger = new Mock<ILogger<TodoItemsController>>();
_controller = new TodoItemsController(_mockRepository.Object, _mockLogger.Object);
}
[Fact]
public async Task GetTodoItems_ReturnsOkResult_WithListOfTodoItems()
{
// Arrange
var todoItems = new List<TodoItem>
{
new TodoItem { Id = 1, Name = "Test Item 1", IsComplete = false },
new TodoItem { Id = 2, Name = "Test Item 2", IsComplete = true }
};
_mockRepository.Setup(repo => repo.GetAllAsync()).ReturnsAsync(todoItems);
// Act
var result = await _controller.GetTodoItems();
// Assert
var okResult = Assert.IsType<ActionResult<IEnumerable<TodoItem>>>(result);
var returnValue = Assert.IsType<List<TodoItem>>(okResult.Value);
Assert.Equal(2, returnValue.Count);
}
[Fact]
public async Task GetTodoItem_ReturnsNotFound_WhenItemDoesNotExist()
{
// Arrange
_mockRepository.Setup(repo => repo.GetByIdAsync(1)).ReturnsAsync((TodoItem)null);
// Act
var result = await _controller.GetTodoItem(1);
// Assert
Assert.IsType<NotFoundResult>(result.Result);
}
[Fact]
public async Task CreateTodoItem_ReturnsCreatedAtAction_WithCreatedItem()
{
// Arrange
var newItem = new TodoItem { Name = "New Item", IsComplete = false };
var createdItem = new TodoItem { Id = 1, Name = "New Item", IsComplete = false };
_mockRepository.Setup(repo => repo.AddAsync(newItem)).ReturnsAsync(createdItem);
// Act
var result = await _controller.CreateTodoItem(newItem);
// Assert
var createdAtActionResult = Assert.IsType<CreatedAtActionResult>(result.Result);
Assert.Equal("GetTodoItem", createdAtActionResult.ActionName);
Assert.Equal(createdItem, createdAtActionResult.Value);
}
}
12.2 集成测试
设置集成测试
public class TodoApiIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
private readonly HttpClient _client;
public TodoApiIntegrationTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
_client = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
// 移除真实的数据库上下文
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<TodoContext>));
if (descriptor != null)
services.Remove(descriptor);
// 添加内存数据库
services.AddDbContext<TodoContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
});
}).CreateClient();
}
[Fact]
public async Task Get_TodoItems_ReturnsSuccessStatusCode()
{
// Act
var response = await _client.GetAsync("/api/todoitems");
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal("application/json; charset=utf-8",
response.Content.Headers.ContentType?.ToString());
}
[Fact]
public async Task Post_TodoItem_ReturnsCreatedStatusCode()
{
// Arrange
var newItem = new { Name = "Test Item", IsComplete = false };
var json = JsonSerializer.Serialize(newItem);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/todoitems", content);
// Assert
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
}
}
12.3 API测试工具
使用HTTP REPL
# 安装HTTP REPL
dotnet tool install -g Microsoft.dotnet-httprepl
# 连接到API
httprepl https://localhost:7xxx
# 浏览API
ls
# 导航到端点
cd api/todoitems
# 发送GET请求
get
# 发送POST请求
post -c "{"name": "Test Item", "isComplete": false}"
Postman集合
创建Postman集合来测试API:
{
"info": {
"name": "Todo API",
"description": "API测试集合"
},
"item": [
{
"name": "Get All Todos",
"request": {
"method": "GET",
"url": "{{baseUrl}}/api/todoitems"
}
},
{
"name": "Create Todo",
"request": {
"method": "POST",
"url": "{{baseUrl}}/api/todoitems",
"body": {
"mode": "raw",
"raw": "{\"name\": \"Test Item\", \"isComplete\": false}",
"options": {
"raw": {
"language": "json"
}
}
}
}
}
],
"variable": [
{
"key": "baseUrl",
"value": "https://localhost:7xxx"
}
]
}


浙公网安备 33010602011771号