c# - 如何使用 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();
}
}
解决方案
我想提出一个替代解决方案,其中涉及更改要测试的代码的设计。
当前显示的测试用例与外部 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 文档以获取更多示例。
推荐阅读
- python - 这个 Python 脚本返回 "KeyError: '6'" 我不知道为什么
- ios - iOS 推送通知自定义声音
- typescript - 通用对象参数作为其文字
- c++ - 创建大小小于 1MiB 的静态二进制文件,可以从 S3 或 GCS 下载文件
- css - 如何制作响应式网格
- r - 在 R 中使用 Cowplot 或 ggpubs 对齐原点和 X 轴
- salesforce - 多个 AggregateResult 查询
- reactjs - TypeError:instance.render 不是函数(finishClassComponent)
- r - 使用 Sweave 在 LaTeX 文档中实现 R 代码。é 和 è 等字符无法正确显示
- bootstrap-4 - 应用于 fort-awesome 图标的引导工具提示放置和触发选项在 Angular 7 模板中不起作用