简体   繁体   English

如何使用 nunit 使用 httpclient PostAsync 和 GetAsync 对代码进行单元测试

[英]How to Unit-test code with httpclient PostAsync and GetAsync using nunit

I have an ApiController where I have 2 calls for getting response from external APis- httpClient.PostAsync and httpClient.GetAsync.我有一个 ApiController,我有 2 个调用从外部 APIs-httpClient.PostAsync 和 httpClient.GetAsync 获取响应。

Can i get some suggestions for UnitTesting(Nunit) - httpClient.PostAsync and httpClient.GetAsync interactions.我可以获得有关 UnitTesting(Nunit) - httpClient.PostAsync 和 httpClient.GetAsync 交互的一些建议吗?

Code Snippet below-下面的代码片段-

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

I would suggestion to split of the api functionality and your logic.我建议拆分 api 功能和您的逻辑。

Move both http calls to the logic and create the HttpClient in the constructor and don't forget dispose.将两个 http 调用移动到逻辑并在构造函数中创建 HttpClient 并且不要忘记处理。

Reason for this: 1. you can write route test without implementing all the HttpClient stuff 2. You can test you HttpClient calls (post, get) with an mocked http handler or mock the HttpClient -> both is possible if you inject on of them via the constructor in the logic原因: 1. 你可以编写路由测试而不实现所有 HttpClient 的东西 2. 你可以用一个模拟的 http 处理程序测试你的 HttpClient 调用(发布,获取)或模拟 HttpClient -> 如果你注入它们,两者都是可能的通过逻辑中的构造函数

Please implement the async - await pattern correct.请正确实现异步 - 等待模式。

I did a quick split - I would handle the logic like this (see below) and call this logic from the api.我做了一个快速拆分 - 我会处理这样的逻辑(见下文)并从 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

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM