#c# #unit-testing #asp.net-core #asp.net-core-webapi #moq
#c# #модульное тестирование #asp.net-ядро #asp.net-core-webapi #moq
Вопрос:
У меня проблемы с издевательством Moq
. Обычно, имея a HttpClient
, я бы издевался над ним, вводя HttpClient
в базовый класс следующим образом:
public class MyClass
{
private readonly HttpClient httpClient;
public MyClass(HttpClient httpClient)
{
this.httpClient = httpClient;
}
}
Но теперь у меня есть разные функции в моем классе MyClass
, которым нужен такой пользовательский интерфейс HttpClientHandler
:
HttpClientHandler httpClientHandler = new HttpClientHandler();
...
using var client = new HttpClient(httpClientHandler);
Если бы я просто ввел a HttpClient
в MyClassTest
с var service = new MyClass(httpMock.Object);
помощью, тогда HttpClient был бы перезаписан.
Каков был бы правильный способ протестировать мои функции и не выполнять реальный HTTP-вызов?
Комментарии:
1. Вы должны заглянуть в
IHttpClientFactory
и ввести / назватьHttpClient
инъекцию.2. 1) Создайте перегрузку только для тестирования, в которую вы можете передать пользовательский обработчик. Я использовал этот подход с большим успехом в течение многих лет. 2) Абстрагируйте класс от HttpClient и введите его вместо этого. Теоретически ваши абстракции должны быть разработаны для интерфейсов, и вы можете скрыть тот факт, что они даже используют HTTP.
3. Я пошел с
public MyClass(HttpClientHandler httpClientHandler = null)
, а затем вызвал его с помощью макета:var mockHttpMessageHandler = new Mock<HttpClientHandler>();
. Он выполняет свою работу, я просто не уверен, насколько это «чистая» или «лучшая практика».
Ответ №1:
Я полагаю, вы используете типизированный клиентский подход IHttpClientFactory
. Вот почему ваш MyClass
ctor получает HttpClient
экземпляр.
Если вам нужно издеваться над этим HttpClient
, я предлагаю вам последовать совету Хамида Мосаллы.
Короче говоря, есть вспомогательный класс, который делает HttpMessageHandler
SendAsync
его макетируемым (без необходимости использования Moq.Защищено).
public class FakeHandler: HttpMessageHandler
{
public virtual HttpResponseMessage Send(HttpRequestMessage request)
{
throw new NotImplementedException();
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(Send(request));
}
}
Вы можете использовать этот вспомогательный класс, подобный этому, для HttpClient
моделирования любого вызова:
var httpResponse = new HttpResponseMessage
{
Content = new StringContent(JsonConvert.SerializeObject(responseObject))
};
var mockHandler = new Mock<FakeHandler> { CallBase = true };
mockHandler
.Setup(handler => handler.Send(It.IsAny<HttpRequestMessage>()))
.Returns(httpResponse);
var mockHttpClient = new HttpClient(mockHandler.Object);
var SUT = new MyClass(mockHttpClient);
Комментарии:
1. Это тот же подход, который предложил @Andy. Я обязательно рассмотрю это, когда у меня будет больше времени, так как это кажется более «чистым».
Ответ №2:
Каков был бы правильный способ протестировать мои функции и не выполнять реальный HTTP-вызов?
Возможно, это не то, что вы ищете, но я предлагаю вам рассмотреть мудрость Эндрю Локка — не тестируйте контроллеры API / MVC в ASP.NET Ядро.
Для .NET Core (и .NET 5) вам следует избегать издевательства над HttpClient, если вы тестируете класс контроллера.
Если класс контроллера не является вашим SUT, я бы обернул HttpClient в интерфейс фасада и издевался над этим.
Комментарии:
1. Ну, я тестирую базовый класс обслуживания для контроллера. Например, выборка и обработка данных с другой страницы. Затем результат передается контроллеру. Сам контроллер тестируется на основе макетного интерфейса сервисного уровня. На мой взгляд, класс Service должен быть протестирован, и для этого мне нужен
HttpClient
-mock