使用外部依赖项测试操作方法

问题描述 投票:-1回答:1

这是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而不涉及该操作?
  • 实际上,在那种情况下,我们有unitintegrated测试吗?

我想调用实际的外部终点。

API(https://jsonplaceholder.typicode.com/posts)可在Internet上找到,可用于测试目的。

c# unit-testing asp.net-core integration-testing
1个回答
2
投票

这似乎是一个XY problem和混合的担忧。

被测代码与实现问题紧密相关,应该将外部调用封装在服务抽象之后,可以在隔离单元测试期间进行模拟。

应遵循的一些重构步骤....

在测试中构建的那些模型应该在行动中。

[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>();

这样就可以正确地注入从属控制器。

© www.soinside.com 2019 - 2024. All rights reserved.