0

我们的 C# 项目中有一些类可以调用 3rd 方 API。我们使用 HttpClient 对象进行调用。我们已经设置了我们的类,我们在其中执行这些调用以接受 HttpClient,以便在测试时,我们可以在客户端使用自定义/假 DelegatingHandler。

我们已经这样设置了我们的类:

public class CallingService : ApiService
{
    private readonly ISomeOtherService _someOtherService;

    public CallingService (ILogger logger, 
        IConfigurationManager configurationManager,
        ISomeOtherService someOtherService) : base(logger, configurationManager)
    {
        _someOtherService = someOtherService;
    }

    public CallingService (ILogger logger,
        HttpClient client,
        IConfigurationManager configurationManager,
        ISomeOtherService someOtherService) : base(logger, configurationManager, client)
    {
        _someOtherService = someOtherService;
    }

    private async Task<XmlNodeList> TransmitToApi(string xml_string)
    {
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12;
        //..
        string type = "application/xml";
        var content = new StreamContent(new MemoryStream(Encoding.ASCII.GetBytes(xml_string)));
        var targetUri = new Uri(ConfigurationManager.GetAppSetting("ApiUrl"));
        var message = new HttpRequestMessage
        {
            RequestUri = targetUri ,
            Method = HttpMethod.Post,
            Content = content
        };

        message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
        message.Content.Headers.Add("Content-Type", type);
        message.Headers.Add("someHeader", someData);

        HttpResponseMessage response = null;
        try
        {
            // Define the cancellation token.
            CancellationTokenSource source = new CancellationTokenSource();
            CancellationToken token = source.Token;
            response = await Client.SendAsync(message, token);
        }
        catch (Exception ex)
        {
            throw ex;
        }
        //...
        return someData;
    }

ApiService如果未提供,则基类定义一个通用 HttpClient 对象。

我们目前正在使用 SendAsync,因此我们可以定义消息头。(我们的标题比此处列出的要多。)

测试定义 DelegatingHandler 如下:

public class FakeResponseHandler : DelegatingHandler
{
    private readonly Dictionary<Uri, HttpResponseMessage> _fakeResponses = new Dictionary<Uri, HttpResponseMessage>();

    public void AddFakeResponse(Uri uri, HttpResponseMessage responseMessage, string content = "", bool asXml = false)
    {
        if (!string.IsNullOrWhiteSpace(content))
        {
            if (asXml)
            {
                responseMessage.Content = new StringContent(content, Encoding.UTF8, "application/xml");
            }
            else
            {
                responseMessage.Content = new StringContent(content, Encoding.UTF8, "application/json");
            }
        }
        _fakeResponses.Add(uri, responseMessage);
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (_fakeResponses.ContainsKey(request.RequestUri))
        {
            return _fakeResponses[request.RequestUri];
        }
        return new HttpResponseMessage(HttpStatusCode.NotFound) { RequestMessage = request };
    }
}

接着:

[Fact]
public async Task ItWillDoStuffAndCallApi()
{
    using (var mock = AutoMock.GetLoose())
    {
        mock.Mock<IConfigurationManager>()
            .Setup(cm => cm.GetAppSetting("ApiUrl"))
            .Returns("http://example.org/test/");

        string testReturnData = GetFileContents("IntegrationTests.SampleData.SampleApiResponseXML.txt");
        FakeResponseHandler fakeResponseHandler = new FakeResponseHandler();
        fakeResponseHandler.AddFakeResponse(new Uri("http://example.org/test/"),
            new HttpResponseMessage(HttpStatusCode.OK),
            testReturnData,
            true);

        //HttpClient httpClient = new HttpClient(fakeResponseHandler);
        HttpClient httpClient = HttpClientFactory.Create(fakeResponseHandler);
        mock.Provide(httpClient);

        var ourService = new CallingService();
        ourService.TransmitToApi(someXmlString);
    }
}

当我们运行测试时,我们会收到以下消息:

处理程序没有返回响应消息。

而且我们似乎从来没有进入DelegatingHandler.SendAsync方法。

我们还有其他使用HttpClient.PostAsyncor调用 API 的类GetAsync,它们确实调用了该DelegatingHandler.SendAsync方法并按预期工作。

我们尝试过:

HttpClient httpClient = new HttpClient(fakeResponseHandler);


HttpClient httpClient = HttpClientFactory.Create(fakeResponseHandler);

我们还尝试Client.SendAsync了使用和不使用取消令牌。

为什么这不起作用?

我们应该重写这个来使用PostAsync吗?

4

1 回答 1

0

我需要查看内部的实现HttpClientFactory.CreateClient.SendAsync实际执行的操作,但是我仍然能够使用您提供的示例代码并填写我可以让以下内容起作用的空白:

public class FakeResponseHandler : DelegatingHandler
{
    private readonly Dictionary<Uri, HttpResponseMessage> _fakeResponses = new Dictionary<Uri, HttpResponseMessage>();

    public void AddFakeResponse(Uri uri, HttpResponseMessage responseMessage, string content = "", bool asXml = false)
    {
        if (!string.IsNullOrWhiteSpace(content))
        {
            if (asXml)
            {
                responseMessage.Content = new StringContent(content, Encoding.UTF8, "application/xml");
            }
            else
            {
                responseMessage.Content = new StringContent(content, Encoding.UTF8, "application/json");
            }
        }
        _fakeResponses.Add(uri, responseMessage);
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var emptyContent = string.Empty;

        if (request.Content.Headers.ContentType.MediaType == "application/xml")
            emptyContent = "<empty />";

        return Task.FromResult(_fakeResponses.ContainsKey(request.RequestUri) ?
            _fakeResponses[request.RequestUri] :
            new HttpResponseMessage(HttpStatusCode.NotFound)
            {
                RequestMessage = request,
                Content = new StringContent(emptyContent)
            });
    }
}

只是为了让事情变得干净,使用 Task.FromResult 返回一个任务SendAsync并提供一个空内容以避免空引用异常。

public class CallingService
{
    private readonly HttpClient _httpClient;
    private readonly IConfigurationManager _configurationManager;

    public CallingService(HttpClient httpClient, 
        IConfigurationManager configurationManager)
    {
        _httpClient = httpClient;
        _configurationManager = configurationManager;
    }

    public async Task<XmlNodeList> TransmitToApi(string xml_string)
    {
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12;
        //..
        string type = "application/xml";
        var content = new StreamContent(new MemoryStream(Encoding.ASCII.GetBytes(xml_string)));
        var targetUri = new Uri(_configurationManager.GetAppSetting("ApiUrl"));
        var message = new HttpRequestMessage
        {
            RequestUri = targetUri,
            Method = HttpMethod.Post,
            Content = content
        };

        message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
        message.Content.Headers.Add("Content-Type", type);

        string somedata;
        try
        {
            // Define the cancellation token.
            CancellationTokenSource source = new CancellationTokenSource();
            CancellationToken token = source.Token;
            var response = await _httpClient.SendAsync(message, token);

            somedata = await response.Content.ReadAsStringAsync();
        }
        catch (Exception ex)
        {
            throw ex;
        }
        //...
        var xmlDoc = new XmlDocument();
        xmlDoc.LoadXml(somedata);
        return xmlDoc.SelectNodes("*");
    }
}

HttpClient然后测试通过to的实例CallingService

    [TestMethod]
    public async Task TestMethod1()
    {
        const string content = @"<root><test>1243</test></root>";
        const string httpExample = "http://example.org/test/";

        var configurationManager = new Mock<IConfigurationManager>();

        configurationManager
            .Setup(cm => cm.GetAppSetting("ApiUrl"))
            .Returns(httpExample);

        var fakeResponseHandler = new FakeResponseHandler();
        fakeResponseHandler.AddFakeResponse(new Uri(httpExample),
            new HttpResponseMessage(HttpStatusCode.OK), content, true);

        using (var httpClient = new HttpClient(fakeResponseHandler))
        {
            var ourService = new CallingService(httpClient, configurationManager.Object);

            var result = await ourService.TransmitToApi(content);

            Assert.AreEqual(content, result.Item(0)?.OuterXml);
        }
    }

这一切都有效,所以如果我不得不猜测 - 问题将在您的HttpClientFacotry.

希望有帮助!!干杯,:)

于 2017-10-25T15:33:15.237 回答