0
0
Lập trình
NM

Kiểm thử API .NET với Testcontainers và Mockoon

Đăng vào 1 tháng trước

• 8 phút đọc

Kiểm thử API .NET với Testcontainers và Mockoon

Xây dựng các dịch vụ đáng tin cậy không chỉ đơn thuần là viết mã sạch. Nó đòi hỏi một chiến lược kiểm thử mạnh mẽ để xác thực cách ứng dụng của bạn hoạt động trong các tình huống thực tế. Trong khi các bài kiểm tra đơn vị đảm bảo rằng logic kinh doanh hoạt động độc lập, các bài kiểm tra tích hợp xác thực các thành phần khác nhau của một dịch vụ, yêu cầu sử dụng các phụ thuộc bên ngoài như cơ sở dữ liệu, nhà cung cấp đám mây hoặc API bên thứ ba.

Giới thiệu về Testcontainers và Mockoon

Đây là lúc các công cụ như TestcontainersMockoon trở nên vô giá. Testcontainers cho phép bạn khởi động các thành phần cơ sở hạ tầng thực sự, có thể tái sử dụng trong các container nhẹ, mang đến cho các bài kiểm tra tích hợp của bạn một môi trường thực tế. Trong khi đó, Mockoon cung cấp một cách đơn giản nhưng mạnh mẽ để mô phỏng các API bên ngoài, cho phép bạn kiểm soát phản hồi, kiểm tra các điều kiện lỗi và xác thực cách ứng dụng của bạn xử lý các tình huống biên.

Trong bài viết này, chúng ta sẽ điều chỉnh một dự án .NET để sử dụng các mock được tạo bằng Mockoon và khởi động các container để tiêu thụ chúng trong bối cảnh kiểm thử API.

Bối cảnh API .NET

Trong ví dụ này, chúng ta sẽ giả định rằng chúng ta có một API đơn giản cung cấp một endpoint để gửi SMS bằng dịch vụ Twilio. Cấu trúc của dự án được đơn giản hóa: chúng ta có một API tối thiểu với một endpoint, và cấu hình sau trong Program.cs:

csharp Copy
var builder = WebApplication.CreateBuilder(args);

// Đăng ký cấu hình
builder.Services.Configure<TwilioOptions>(
    builder.Configuration.GetSection("Twilio"));

builder.Services.AddScoped<ITwilioService, TwilioService>();

var app = builder.Build();

app.MapPost("/send", async (
    [FromServices] ITwilioService twilioService,
    [FromBody] MessageRequest request) =>
{
    var result = await twilioService.SendMessageAsync(request);
    return result ? Results.Ok() : Results.BadRequest();
});

app.Run();

Để quản lý một URL cơ sở khác cho dịch vụ Twilio khi chạy các bài kiểm tra hoặc khi gỡ lỗi/triển khai API, Options Pattern có thể được sử dụng, cho phép thay đổi các giá trị mặc định theo từng môi trường của các cài đặt cấu hình được gán trong tệp appsettings.json. Ví dụ về triển khai dịch vụ có thể trông như sau:

csharp Copy
public class TwilioService : ITwilioService
{
    private readonly HttpClient _httpClient;
    private readonly IOptions<TwilioConfigurations> _config;

    public TwilioService(IOptions<TwilioConfigurations> config,
        HttpClient httpClient)
    {
        _config = config;
        _httpClient = httpClient;
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_config.Value.AccountSid}:{_config.Value.AuthToken}"))}");
    }
    public async Task SendSms(string message, string to)
    {
        var url = $"{_config.Value.BaseUrl}/Accounts/{_config.Value.AccountSid}/Messages.json";

        var request = new HttpRequestMessage(HttpMethod.Post, url);

        var collection = new List<KeyValuePair<string, string>>
        {
            new("To", to),
            new("From", _config.Value.From),
            new("Body", message)
        };

        request.Content = new FormUrlEncodedContent(collection);

        var response = await _httpClient.SendAsync(request);

        if (!response.IsSuccessStatusCode)
        {
            throw new Exception("Failed to send SMS");
        }
    }
}

Nếu endpoint SendMessage được tiêu thụ, nó sẽ sử dụng các cấu hình appsettings, vì vậy SMS sẽ được gửi thông qua URL dịch vụ Twilio thực sự được cấu hình. Khi dịch vụ đã sẵn sàng, đã đến lúc tạo định nghĩa mock.

Mô phỏng API REST với Mockoon

Mockoon là một công cụ mạnh mẽ cho phép tạo ra các mock khác nhau của các API và Websockets. Trong ví dụ này, chúng ta sẽ mô phỏng endpoint Gửi tin nhắn của Twilio với 2 trường hợp: phản hồi ✅ OK và phản hồi ❌ BadRequest. Số lượng phản hồi và quy tắc sẽ phụ thuộc vào số lượng tình huống cần thiết để kiểm thử mã của chúng ta. Trong giao diện người dùng của Mockoon, các tình huống có thể trông như sau:

Tạo và xây dựng hình ảnh mock

Khi định nghĩa JSON của Mockoon cho dịch vụ cần kiểm thử đã sẵn sàng, đã đến lúc xây dựng hình ảnh Docker sẽ khởi động máy chủ Mockoon và mở rộng API mô phỏng trong container. Trong trường hợp này, có thể sử dụng một hình ảnh Node đơn giản, cài đặt các phụ thuộc Mockoon CLI và sao chép định nghĩa JSON Mockoon. Dockerfile có thể trông như sau:

dockerfile Copy
FROM node:18-alpine

RUN npm install -g @mockoon/cli@9.3.0

WORKDIR /app

COPY TwilioMocks.json /app/TwilioMocks.json

EXPOSE 3000

ENTRYPOINT ["mockoon-cli", "start", "--data", "/app/TwilioMocks.json", "--port", "3000"]

Tích hợp Kiểm thử và Testcontainers

Testcontainers là một thư viện mã nguồn mở tuyệt vời cho phép chúng ta chạy các container Docker, xử lý việc tạo và xóa các container, cho phép tạo cơ sở dữ liệu hoặc bất kỳ loại dịch vụ nào có thể chạy trong container.

Bây giờ, khi mock của dịch vụ và hình ảnh đã được xây dựng với định nghĩa mock đã sẵn sàng, bước tiếp theo là tạo lớp kiểm thử cho dự án và thêm logic Testcontainers để khởi động các container. Hãy bắt đầu với lớp WebApplicationFactory của chúng ta:

csharp Copy
public class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
    private readonly IContainer _mockoonContainer;

    public CustomWebApplicationFactory()
    {
        _mockoonContainer = new ContainerBuilder()
            .WithImage("mockoon-twilio:latest")
            .WithPortBinding(3000, true)
            .Build();
    }

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services =>
        {
            // Ghi đè URL Twilio để trỏ đến container Mockoon
            services.PostConfigure<TwilioOptions>(options =>
            {
                options.BaseUrl = _mockoonContainer.GetMappedPublicPort(3000).ToString();
            });
        });
    }
}

Trong lớp này, chúng ta thực hiện trừu tượng WebApplicationFactory với lớp Program của chúng ta làm điểm vào. Điều này sẽ cho phép thêm các tiêm phụ thuộc mới hoặc thay đổi các giá trị của các phụ thuộc hiện có. Trong trường hợp này, chúng ta sẽ sử dụng nó để khởi động container bằng cách sử dụng TestContainers, sau đó thay đổi URL dịch vụ Twilio thành URL máy chủ của container. Để sử dụng triển khai này, chúng ta tạo lớp SetUpFixture:

csharp Copy
[SetUpFixture]
public class TestSetup
{
    private CustomWebApplicationFactory _factory;

    [OneTimeSetUp]
    public async Task OneTimeSetup()
    {
        _factory = new CustomWebApplicationFactory();
        await _factory.Services.GetRequiredService<IHostedService>().StartAsync(CancellationToken.None);
    }

    [OneTimeTearDown]
    public async Task OneTimeTearDown()
    {
        await _factory.DisposeAsync();
    }
}

Lớp này sẽ được thực thi trước khi chạy các bài kiểm thử, điều này được chỉ định bởi thuộc tính SetUpFixture của NUnit. Bằng cách sử dụng WebApplicationFactory, có thể tạo một thể hiện HttpClient sẽ được sử dụng để tiêu thụ API tối thiểu trong kiểm thử. Trong bài viết này, chỉ có 2 tình huống sử dụng HttpClient:

csharp Copy
[TestFixture]
public class MessageTests
{
    private HttpClient client = new();

    [SetUp]
    public void Setup()
    {
        client = TestStartup.HttpClient;
    }

    [Test]
    public async Task SendMessage_WhenMessageIsSent_ShouldReturnSuccess()
    {
        var request = new MessageRequestModel
        {
            Message = "This test should pass",
            To = "+19514004113"
        };

        var response = await client.PostAsJsonAsync("/api/v1/messages", request);
        TestContext.Out.WriteLine(await response.Content.ReadAsStringAsync());

        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
    }

    [Test]
    public async Task SendMessage_WhenMessageIsNotSent_ShouldReturnError()
    {
        var request = new MessageRequestModel
        {
            Message = "This test should pass when an error occurs",
            To = "+1838383838" // Số điện thoại mà mock sẽ phản hồi với lỗi 400
        };

        var response = await client.PostAsJsonAsync("/api/v1/messages", request);

        Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.InternalServerError));
    }
}

Kết quả của các bài kiểm tra trông như sau:

Kết luận

Tóm lại, việc sử dụng TestcontainersMockoon để kiểm thử dịch vụ cung cấp một sự cân bằng giữa thực tếtốc độ. Testcontainers đảm bảo rằng các bài kiểm tra tích hợp của bạn tiếp cận các phụ thuộc thực sự trong container, làm cho các bài kiểm tra của bạn rất đáng tin cậy và phản ánh môi trường sản xuất.

Trong khi đó, Mockoon cho phép mô phỏng các endpoint REST với độ chính xác và linh hoạt — cho dù bạn cần phản hồi tĩnh, lập trình động, proxy hay quy tắc nâng cao.

Tài liệu tham khảo

Bài viết này được chuyển thể từ bài viết gốc trên Medium.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào