Издевательская оболочка HttpClient, возвращающая null с помощью xunit

Я пытаюсь привыкнуть к модульному тестированию с ядром .net. К сожалению, я застрял на том, чтобы моя оболочка HttpClient возвращала неудавшийся HttpResponseMessage.

Вот мой тест, который я пытаюсь заставить работать:

[Fact]
public async void TestHttpExceptionOnBadRequest()
{
    using (var stream = new MemoryStream(Encoding.UTF8.GetBytes("Test")))
    {
        var xmlSerializer = new Mock<IXmlSerializer>();
        xmlSerializer.Setup(serializer => serializer.Deserialize(stream)).Returns(new object());

        var httpClient = new Mock<IHttpHandler>();
        httpClient.Setup(client => client.GetAsync("Test"))
            .Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.BadRequest)));

        var postcodeLookup = new PostcodeLookupService(xmlSerializer.Object, httpClient.Object, "", "");
        await Assert.ThrowsAsync<HttpRequestException>(async () => await postcodeLookup.SearchAsync("", ""));
    }
}

Ниже приведен мой сервисный код. Проблема в том, что response.EnsureSuccessStatusCode(); возвращает null.

public async Task<PostcodeContainer> SearchAsync(string text, string container)
{
    // add the query parameters to the query string
    var query = _baseQueryString + "&text=" + WebUtility.UrlEncode(text);
    query += "&container=" + WebUtility.UrlEncode(container);

    // initiate client & response
    var response = await _httpClient.GetAsync(query);
    response.EnsureSuccessStatusCode();

    return (PostcodeContainer)_serializer.Deserialize(await response.Content.ReadAsStreamAsync());
}

И для полноты вот мое определение IHttpHandler для насмешек.

public interface IHttpHandler
{
    HttpResponseMessage Get(string url);
    HttpResponseMessage Post(string url, HttpContent content);
    Task<HttpResponseMessage> GetAsync(string url);
    Task<HttpResponseMessage> PostAsync(string url, HttpContent content);
}

person Richard Vanbergen    schedule 06.04.2017    source источник


Ответы (1)


Это связано с тем, что ожидаемая настройка запроса не соответствует тому, что генерируется в тестируемом методе.

// add the query parameters to the query string
var query = _baseQueryString + "&text=" + WebUtility.UrlEncode(text);
query += "&container=" + WebUtility.UrlEncode(container);

Это приводит к тому, что макет возвращает null, поскольку он не знает, что делать, когда получает неожиданный запрос, который не соответствует "Test".

Итак, если предположить, что _baseQueryString также является пустой строкой на основе теста в примере, сгенерированный запрос может закончиться как

&text=&container=

Следовательно, это то, что должен ожидать макет, чтобы работать должным образом. Кроме того, учитывая, что тестируемый метод, как ожидается, выдаст ошибку до того, как сериализатор что-либо сделает, нет необходимости настраивать его для каких-либо действий. Просто издевайтесь над этим и передайте макет.

Вы также можете заставить клиента ожидать любой запрос, используя It.IsAny<string>() в выражении настройки.

Наконец, пусть тест возвращает Task при выполнении функций acync.

[Fact]
public async Task TestHttpExceptionOnBadRequest() {

    var xmlSerializer = new Mock<IXmlSerializer>();
    xmlSerializer
        .Setup(serializer => serializer.Deserialize(It.IsAny<Stream>()))
        .Returns(new PostcodeContainer());

    var expectedResponse = new HttpResponseMessage(HttpStatusCode.BadRequest);
    var httpClient = new Mock<IHttpHandler>();
    httpClient
        .Setup(client => client.GetAsync(It.IsAny<string>()))
        .ReturnsAsync(expectedResponse);

    var postcodeLookup = new PostcodeLookupService(xmlSerializer.Object, httpClient.Object, "", "");
    await Assert.ThrowsAsync<HttpRequestException>(async () => await postcodeLookup.SearchAsync("", ""));
}
person Nkosi    schedule 06.04.2017