[英]Testing an action method with external dependencies
這是HomeController
中的Action方法的示例:
[HttpPost]
public async Task<dynamic> UnitTest(string data)
{
var httpClient = new HttpClient();
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"));
string responseContent = await response.Content.ReadAsStringAsync();
return responseContent;
}
我想測試一下,但是我不知道如何做。 我嘗試了以下方法:
[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
Assert.AreEqual(1,
responseModel.UserId);
}
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; }
}
不幸的是,上述方法不起作用。 由於我很困惑,您能給我以下答案嗎?
UnitTest
操作的最佳方法是什么? 我的方法錯了嗎? 我是否只需要從JsonRightTest方法調用API而不涉及操作 ? unit
測試或integrated
測試? 我想稱呼實際的外部終點。
該API( https://jsonplaceholder.typicode.com/posts )可在Internet上找到,並可用於測試目的。
這似乎是一個XY問題和各種顧慮。
被測試的代碼與實現問題緊密相關,並且應該將外部調用封裝在服務抽象之后,該服務抽象可以在隔離的單元測試中進行模擬。
應該遵循的一些重構步驟。
測試中正在構建的那些模型應該起作用。
[HttpPost]
public async Task<IActionResult> UnitTest([FromBody]MyDataR data) {
var httpClient = new HttpClient();
var requestJson = JsonConvert.SerializeObject(data);
var url = "https://jsonplaceholder.typicode.com/posts";
var response = await httpClient.PostAsync(url, new StringContent(requestJson, Encoding.UTF8, "application/json"));
if(response.IsSuccessStatusCode) {
var responseContent = await response.Content.ReadAsStringAsync();
var responseModel = JsonConvert.DeserializeObject<MyModel>(responseContent);
return Ok(responseModel);
}else
return StatusCode(response.StatusCode);
}
進一步重構,應該抽象出外部端點的實際調用
public interface IExternalService {
Task<MyModel> PostDataAsync(MyData data);
}
並據此實施
public class ExternalService : IExternalService {
// should consider abstracting this as well but that is another matter
static Lazy<HttpClient> _httpClient = new Lazy<HttpClient>(() => new HttpClient());
private HttpClient httpClient {
get { return _httpClient.Value; }
}
public async Task<MyModel> PostDataAsync(MyData data) {
var requestJson = JsonConvert.SerializeObject(data);
var url = "https://jsonplaceholder.typicode.com/posts";
var content = new StringContent(requestJson, Encoding.UTF8, "application/json")
var response = await httpClient.PostAsync(url, content);
var responseContent = await response.Content.ReadAsStringAsync();
if(response.IsSuccessStatusCode) {
var responseContent = await response.Content.ReadAsStringAsync();
var responseModel = JsonConvert.DeserializeObject<MyModel>(responseContent);
return responseModel;
}else
return null;
}
}
控制器中的動作現在看起來像
private readonly IExternalService externalService; // Assumed injected into the controller
[HttpPost]
public async Task<IActionResult> UnitTest([FromBody]MyDataR data) {
var responseModel = await externalService.PostDataAsync(data);
if(responseModel != null) {
return Ok(responseModel);
}else
return BadRequest();
}
通過消除與外部服務調用的緊密耦合,這將允許對控制器進行隔離測試,以驗證其性能是否符合預期。
如果希望檢查外部端點的行為是否符合預期,現在可以自行測試外部服務調用實現。 由於它依賴於實際的外部端點,因此可以認為是集成測試。
[TestMethod]
public async Task JsonRightTest() {
// Arrange
var expected = 1;
var model = new MyModelR {
Title = "foo",
Body = "bar",
UserId = 1
};
var target = new ExternalService(); // System under test
// Act
var responseModel = await target.PostDataAsync(model);
// Assert
Assert.IsNotNull(responseModel);
var actual = responseModel.UserId;
Assert.AreEqual(expected, actual);
}
現在,這應該可以更輕松地檢查外部服務,以驗證其行為是否符合預期。
在生產中,您將確保在組合根目錄中注冊了外部服務抽象及其實現。
services.AddTransient<IExternalService, ExternalService>();
以便將其正確注入到從屬控制器中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.