首页 > 解决方案 > 如何使用 ASP.NET Core 对外部 API 进行集成测试

问题描述

我正在尝试对外部 API 进行一些集成测试。我在网上找到的大多数指南都是关于测试 ASP.NET Web api 的,但是关于外部 API 的内容并不多。我想在这个 API 上测试一个 GET 请求,并通过检查状态码是否正常来确认它是否通过。然而,这个测试没有通过,我想知道我是否正确地做到了这一点。目前它给了我一个状态码 404(未找到)。

我正在与您如何建议我测试外部 APIxUnit一起使用?Microsoft.AspNetCore.TestHost

private readonly HttpClient _client;

public DevicesApiTests()
{
    var server = new TestServer(new WebHostBuilder()
        .UseEnvironment("Development")
        .UseStartup<Startup>());
    _client = server.CreateClient();
}

[Theory]
[InlineData("GET")]
public async Task GetAllDevicesFromPRTG(string method)
{
    //Arrange
    var request = new HttpRequestMessage(new HttpMethod(method), "https://prtg.nl/api/content=Group,Device,Status");

    //Act
    var response = await _client.SendAsync(request);

    // Assert
    response.EnsureSuccessStatusCode();
    Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}

编辑

我试图测试的 API 调用如下所示,并且工作正常

private readonly DbContext _dbContext;
private readonly IDevicesRepository _devicesRepository;

public DevicesAPIController(DbContext dbContext, IDevicesRepository devicesRepository)
{
    _dbContext = dbContext;
    _devicesRepository = devicesRepository;
}

[HttpPost("PostLiveDevicesToDatabase")]
public async Task<IActionResult> PostLiveDevicesToDatabase()
{
    try
    {

        using (var httpClient = new HttpClient())
        {
            httpClient.DefaultRequestHeaders.Clear();
            httpClient.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));

            using (var response = await httpClient
                .GetAsync(
                    "https://prtg.nl/api/content=Group,Device,Status")
            )
            {
                string apiResponse = await response.Content.ReadAsStringAsync();
                var dataDeserialized = JsonConvert.DeserializeObject<Devices>(apiResponse);

                devicesList.AddRange(dataDeserialized.devices);

                foreach (DevicesData device in devicesList)
                {

                    _dbContext.Devices.Add(device);
                    devicesAdded.Add(device);

                    _dbContext.SaveChanges();
                }
            }
        }
    }
    catch
    {
        return BadRequest();
    }
}

标签: c#asp.net-coreintegration-testingxunit

解决方案


我想提出一个替代解决方案,其中涉及更改要测试的代码的设计。

当前显示的测试用例与外部 API 耦合并测试其响应能力200 OK而不是您的代码(即,您的代码根本没有被引用)。这也意味着,如果无法建立与服务器的连接(例如,可能是 CI/CD 管道中的隔离构建代理,或者只是一个不稳定的咖啡馆 WIFI),则测试会因断言之外的其他原因而失败。

我建议将HttpClient及其特定于 API 的配置提取到抽象中,就像您对 所做的那样IDevicesRepository(尽管示例中没有使用它)。这允许您替换来自 API 的响应并仅测试您的代码。替换可以探索边缘情况,例如连接断开、空响应、格式错误的响应、外部服务器错误等。这样,您可以在代码中使用更多故障路径,并使测试与外部 API 分离。

抽象的实际替换将在测试的“安排”阶段完成。您可以为此使用Moq NuGet 包。

更新

为了提供一个使用 Moq 模拟空 API 响应的示例,请考虑一个假设的抽象,例如:

public interface IDeviceLoader
{
    public IEnumerable<DeviceDto> Get();
}

public class DeviceDto
{
    // Properties here...
}

请记住,示例抽象不是异步的,当您调用 I/O(即网络)时,可以将其视为最佳实践。我跳过它以保持简单。有关如何处理异步方法的信息,请参阅Moq 文档。

为了模拟响应,测试用例的主体可以是:

[Fact]
public async Task CheckEndpointHandlesEmptyApiResponse()
{
    // How you get access to the database context and device repository is up to you.
    var dbContext = ...
    var deviceRepository = ...

    //Arrange
    var apiMock = new Mock<IDeviceLoader>();

    apiMock.Setup(loader => loader.Get()).Returns(Enumerable.Empty<DeviceDto>());

    var controller = new DevicesAPIController(dbContext, deviceRepository, apiMock.Object);

    //Act
    var actionResponse = controller.PostLiveDevicesToDatabase();

    // Assert
    // Check the expected HTTP result here...
}

请检查其存储库(上面链接)上的 Moq 文档以获取更多示例。


推荐阅读