模拟HttpClient有多个请求

时间:2018-04-21 13:48:53

标签: c# httpclient moq

我试图对一个调用API的函数进行单元测试。我已经使用如下的模拟HttpMessageHandler成功完成了此操作,这允许我伪造来自API的响应:

private static Mock<HttpMessageHandler> GetMockHttpMessageHandler(string mockResponse)
{
    var mockMessageHandler = new Mock<HttpMessageHandler>();
    mockMessageHandler.Protected()
        .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
        .Returns(Task.FromResult(new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent(mockResponse)
        }));
    return mockMessageHandler;
}

到目前为止,这么好。我已经能够测试我的一半功能,后半部分再次进行api调用 - 然后两个响应都被包装到系统使用的对象中。问题是,第二个api需要有不同的模拟响应。

我认为我可以将上面代码中的ItExpr.IsAny<HttpRequestMessage>()更改为new HttpRequestMessage(HttpMethod.Post, "http://LiveUrl.com/AuthenticateUserCredential"),然后根据URI更改多个Setup/Returns,但我尝试过了它如下(只有一个Setup/Return来测试我没有打破上半场测试)

private static Mock<HttpMessageHandler> GetMockHttpMessageHandler(string mockResponse)
{
    var mockMessageHandler = new Mock<HttpMessageHandler>();
    mockMessageHandler.Protected()
        .Setup<Task<HttpResponseMessage>>("SendAsync", new HttpRequestMessage(HttpMethod.Post, "http://LiveUrl.com/AuthenticateUserCredential"), ItExpr.IsAny<CancellationToken>())
        .Returns(Task.FromResult(new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.OK,
            Content = new StringContent(mockResponse)
        }));

    return mockMessageHandler;
}

现在上面打破了第一个api调用 - 我得到以下响应:

  

处理程序未返回响应消息

现在我被困 - 这是我试图做的甚至可能吗?

2 个答案:

答案 0 :(得分:1)

调用API的函数不能进行单元测试,这不是单元测试,而是集成测试。 如果你想简化你的生活,就要开始以功能的方式思考。

你有一个调用API的函数,让我们说它看起来像这样:

public string DoSomething()
{
    string myData = //here we call another API
    //here we do something with the response from the API
}

现在,在您的函数之外进行API调用:

string myData = // api call
DoSomething(myData);

public string DoSomething(string myData)
    {
        // here we do something with myData 
        //this function no longer cares how it got the data.
    }

现在您可以对您的功能进行单元测试,您可以检查实际功能和业务规则,而无需担心嘲笑任何事情。一切都简单得多。

您仍然可以使用集成测试来测试实际的API,这些将检查您是否获得了正确的HTTP代码,异常,模型验证是否正确等等。

答案 1 :(得分:0)

在Moq中使用SetupSequence可以解决此问题:

questions

调用上面的代码:

private HttpClient GetHttpClientWithHttpMessageHandlerSequenceResponseMock(List<Tuple<HttpStatusCode,HttpContent>> returns)
    {

        var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
        var handlerPart = handlerMock
           .Protected()
           // Setup the PROTECTED method to mock
           .SetupSequence<Task<HttpResponseMessage>>(
              "SendAsync",
              ItExpr.IsAny<HttpRequestMessage>(),
              ItExpr.IsAny<CancellationToken>()
           );

        foreach (var item in returns)
        {
            handlerPart = AdddReturnPart(handlerPart,item.Item1,item.Item2);
        }
        handlerMock.Verify();
        // use real http client with mocked handler here
        var httpClient = new HttpClient(handlerMock.Object)
        {
            BaseAddress = new Uri("http://test.com/"),
        };
        return httpClient;
    }

    private ISetupSequentialResult<Task<HttpResponseMessage>> AdddReturnPart(ISetupSequentialResult<Task<HttpResponseMessage>> handlerPart,
        HttpStatusCode statusCode, HttpContent content)
    {
        return handlerPart

           // prepare the expected response of the mocked http call
           .ReturnsAsync(new HttpResponseMessage()
           {
               StatusCode = statusCode, // HttpStatusCode.Unauthorized,
               Content = content //new StringContent("[{'id':1,'value':'1'}]"),
           });
    }

}