如何测试含外部依赖的Action方法?现有方案遇阻求指导
如何正确测试调用外部API的ASP.NET Core Action方法?
咱们先逐个拆解你的问题,结合你的代码来分析:
1. 测试该UnitTest Action的最佳方式是什么?
要分两种场景来看:
- 单元测试:如果只想验证Action的逻辑(比如参数处理、调用外部API的方式、响应处理),需要隔离外部依赖(也就是那个真实的外部API),可以通过模拟
HttpClient来实现,这样测试速度快,不依赖外部服务的可用性。 - 集成测试:如果要验证整个流程(从请求进入你的Action,到你的Action调用外部API,再返回响应的完整链路),可以用ASP.NET Core的
WebApplicationFactory来搭建测试服务器,同时可以选择调用真实的外部API(像你用的这个公开测试API是没问题的),或者继续模拟外部API。
2. 你的测试方法是否存在错误?
是的,你的代码里有两个关键问题:
问题1:Action的参数绑定错误
你的UnitTest Action的data参数是string类型,并且没有标记[FromBody],当你用HttpClient发送JSON内容时,ASP.NET Core默认不会把JSON请求体绑定到这个string参数上,导致data的值为null,后续的序列化操作自然也会出错。
问题2:Action里的序列化逻辑错误
假设参数能正确接收,你把传入的string data又用JsonConvert.SerializeObject(data)序列化了一次,这会导致发送给外部API的内容是字符串的JSON转义版本(比如原本的{"Title":"foo"}会变成"{\"Title\":\"foo\"}"),外部API无法解析这种格式,返回的响应自然不符合你的预期。
修正后的Action代码:
[HttpPost] public async Task<dynamic> UnitTest([FromBody] MyModelR data) // 用强类型+FromBody,避免字符串处理错误 { using var httpClient = new HttpClient(); // 建议用using释放资源 var request = JsonConvert.SerializeObject(data); var url = "https://jsonplaceholder.typicode.com/posts"; var response = await httpClient.PostAsync(url, new StringContent(request, Encoding.UTF8, "application/json")); response.EnsureSuccessStatusCode(); // 建议添加,确保请求成功 string responseContent = await response.Content.ReadAsStringAsync(); return responseContent; }
修正后的测试代码(集成测试,用WebApplicationFactory):
// 假设你的项目是ASP.NET Core项目,测试类继承自IClassFixture<WebApplicationFactory<Program>> public class HomeControllerTests : IClassFixture<WebApplicationFactory<Program>> { private readonly HttpClient _client; public HomeControllerTests(WebApplicationFactory<Program> factory) { _client = factory.CreateClient(); } [TestMethod] public async Task JsonRightTest() { MyModelR model1 = new MyModelR { Title = "foo", Body = "bar", UserId = 1 }; string output1 = JsonConvert.SerializeObject(model1); var url = "/Home/UnitTest"; // 注意路径开头的/ var response = await _client.PostAsync(url, new StringContent(output1, Encoding.UTF8, "application/json")); response.EnsureSuccessStatusCode(); var responseContent = await response.Content.ReadAsStringAsync(); var responseModel = JsonConvert.DeserializeObject<MyModel>(responseContent); Assert.AreEqual(1, responseModel.UserId); Assert.AreEqual("foo", responseModel.Title); } } internal class MyModel { public string Title { get; set; } public string Body { get; set; } public int UserId { get; set; } public int Id { get; set; } } internal class MyModelR { public string Title { get; set; } public string Body { get; set; } public int UserId { get; set; } }
3. 是否应该直接在JsonRightTest方法中调用外部API,而非调用Action?
这取决于你的测试目的:
- 如果你的目标是测试自己的Action逻辑(比如Action是否正确接收参数、是否正确调用外部API、是否正确返回响应),那必须调用Action,否则你测试的只是外部API本身,而不是你的代码。
- 如果你的目标只是验证外部API的交互逻辑(比如确认发送特定格式的请求能得到预期响应),那可以直接调用外部API,但这不属于对你的应用代码的测试。
结合你的场景,你应该调用Action来测试你的代码逻辑。
4. 若直接调用外部API,这种测试属于单元测试还是集成测试?
这种测试属于集成测试。因为单元测试要求测试的代码是孤立的,不依赖外部服务、数据库、网络等;而集成测试是验证多个组件(你的代码+外部API服务)之间的交互是否正常,直接调用外部API显然涉及到了外部服务的依赖,所以属于集成测试范畴。
内容的提问来源于stack exchange,提问作者Unknown developer
相关产品推荐
相关产品推荐

