第十二章 测试和调试

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"
    }
  ]
}

wechat_2025-07-31_105805_938


posted @ 2025-08-07 09:11  高宏顺  阅读(230)  评论(0)    收藏  举报