【问题标题】:How to Unit-test code with httpclient PostAsync and GetAsync using nunit如何使用 nunit 使用 httpclient PostAsync 和 GetAsync 对代码进行单元测试
【发布时间】:2019-04-02 20:11:35
【问题描述】:

我有一个 ApiController,我有 2 个调用来获取来自外部 API 的响应 - httpClient.PostAsync 和 httpClient.GetAsync。

我能得到一些关于 UnitTesting(Nunit) - httpClient.PostAsync 和 httpClient.GetAsync 交互的建议吗?

下面的代码片段-

 [System.Web.Http.RoutePrefix("api/v1/testApi")]
    [System.Web.Http.AllowAnonymous]
    public class TestApiController : ApiController
    {
          //get settings data from config file -TestFetchTokenLogin , TestFetchTokenKey , TestFetchTokenUri etc

        [System.Web.Http.Route("gettestapi")]
        [ValidateAntiForgeryToken]
        public IHttpActionResult GetTestAPI(string param1 = "", string param2 = "", string param3 = "", string param4 = "")
        {
            ApiDataResponse dataResponse;
            var httpClient = new HttpClient(new HttpClientHandler { UseProxy = false });
            try
            {
                List<KeyValuePair<string, string>> fetchTokenrequest = new List<KeyValuePair<string, string>>
                {
                    new KeyValuePair<string, string>("login", TestFetchTokenLogin),
                    new KeyValuePair<string, string>("password", TestFetchTokenKey)
                };
                FormUrlEncodedContent fetchTokenrequestBody = new FormUrlEncodedContent(fetchTokenrequest);
                var fetchTokenResponse = httpClient.PostAsync(TestFetchTokenUri, fetchTokenrequestBody);
                if (fetchTokenResponse != null)
                {
                    var tokenResponse = JToken.Parse(fetchTokenResponse.Result.Content.ReadAsStringAsync().Result);
                    var token = tokenResponse?.SelectToken("access_token")?.ToString();
                    if (!string.IsNullOrWhiteSpace(token))
                    {
                        var apiResponse = httpClient.GetAsync($"{TestFetchDataUri}?restapi.session_key={token}&param1={param1}&param2={param2}&sub_param2={param3}&param4={param4}&OutputFormat=json");
                        var task = apiResponse.Result.Content.ReadAsStringAsync();
                        dataResponse = new ApiDataResponse
                        {
                            Success = true,
                            Response = task.Result
                        };
                        return ResponseMessage(Request.CreateResponse(HttpStatusCode.OK, dataResponse));
                    }
                    dataResponse = new ApiDataResponse
                    {
                        Success = false,
                        Response = "access_token not exists in the response"
                    };
                    return ResponseMessage(Request.CreateResponse(HttpStatusCode.NoContent, dataResponse));
                }
                dataResponse = new ApiDataResponse
                {
                    Success = false,
                    Response = "token response empty or null"
                };
                return ResponseMessage(Request.CreateResponse(HttpStatusCode.InternalServerError, dataResponse));
            }
            catch (Exception ex)
            {
                dataResponse = new ApiDataResponse
                {
                    Success = false,
                    Response = ex.Message
                };
                return ResponseMessage(Request.CreateResponse(HttpStatusCode.InternalServerError, dataResponse));
            }
        }
    }
}

【问题讨论】:

    标签: c# unit-testing nunit dotnet-httpclient nunit-3.0


    【解决方案1】:

    我建议拆分 api 功能和您的逻辑。

    将两个 http 调用移至逻辑并在构造函数中创建 HttpClient,不要忘记 dispose。

    原因: 1. 你可以在不实现所有 HttpClient 东西的情况下编写路由测试 2. 您可以使用模拟的 http 处理程序测试您的 HttpClient 调用(发布、获取)或模拟 HttpClient -> 如果您通过逻辑中的构造函数注入它们,则两者都是可能的

    请正确实现 async - await 模式。

    我做了一个快速拆分 - 我将处理这样的逻辑(见下文)并从 api 调用此逻辑。

    public class TestLogic : IDispose {
    
    private HttpClientHandler _Handler;
    
    // I would handle the login and password in a own config object 
    public TestLogic(HttpClientHandler handler, Config config )
    {
        _Handler = handler ?? throw new ArgumentException(); 
    }
    
    public async Task<ApiDataResponse> PostfetchTokenrequestBodyAsync()
    {
        List<KeyValuePair<string, string>> fetchTokenrequest = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("login", TestFetchTokenLogin),
            new KeyValuePair<string, string>("password", TestFetchTokenKey)
        };
        FormUrlEncodedContent fetchTokenrequestBody = new FormUrlEncodedContent(fetchTokenrequest);
        var fetchTokenResponse = await httpClient.PostAsync(TestFetchTokenUri, fetchTokenrequestBody).ConfigurationAwait(false);
        fetchTokenResponse.EnsureSuccessStatusCode();
    
        var tokenResponse = JToken.Parse(await fetchTokenResponse.ReadAsStringAsync().ConfigurationAwait(false));
    
        var token = tokenResponse?.SelectToken("access_token")?.ToString();
    
        return tokenResponse;
    }
    
    public async Task<ApiDataResponse> GetHinterlandFetchDataUriAsync(string token, ...)
    {
        var apiResponse = httpClient.GetAsync($"{HinterlandFetchDataUri}?restapi.session_key={token}&param1={param1}&param2={param2}&sub_param2={param3}&param4={param4}&OutputFormat=json");
        apiResponse.EnsureSuccessStatusCode(); // handle exception 
    
        var result = await apiResponse.Result.Content.ReadAsStringAsync().ConfigurationAwait(false);
    
        return = new ApiDataResponse
        {
            Success = true,
            Response = result
        };
    }
    
    // implement IDisposable interface correct https://docs.microsoft.com/de-de/dotnet/api/system.idisposable?view=netframework-4.7.2
    

    }

    【讨论】:

    • 感谢您的回复。我将整个逻辑保留在内部的唯一原因 - GetTestAPI() - 因为 GetTestApi() 需要从 UI 调用并期望从 httpClient.GetAsync 获取的 jsonResponse 。为了进行 GetAsync() 调用,我还需要从 PostAsync() 获取的令牌密钥 - 所以这两个逻辑都被合并在一个单一的操作方法中。 ——
    猜你喜欢
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-03
    • 2010-09-15
    • 2021-08-26
    • 1970-01-01
    相关资源
    最近更新 更多