Tôi không bị thuyết phục bởi nhiều câu trả lời.
Trước hết, hãy tưởng tượng bạn muốn kiểm tra đơn vị một phương pháp sử dụng HttpClient
. Bạn không nên khởi tạo HttpClient
trực tiếp trong quá trình triển khai của mình. Bạn nên đặt một nhà máy có trách nhiệm cung cấp một phiên bản HttpClient
cho bạn. Bằng cách đó, bạn có thể mô phỏng sau này tại nhà máy đó và trả lại bất kỳ thứ gì HttpClient
bạn muốn (ví dụ: mô phỏng HttpClient
chứ không phải sản phẩm thật).
Vì vậy, bạn sẽ có một nhà máy như sau:
public interface IHttpClientFactory
{
HttpClient Create();
}
Và một triển khai:
public class HttpClientFactory
: IHttpClientFactory
{
public HttpClient Create()
{
var httpClient = new HttpClient();
return httpClient;
}
}
Tất nhiên, bạn sẽ cần phải đăng ký trong IoC Container triển khai này. Nếu bạn sử dụng Autofac, nó sẽ giống như sau:
builder
.RegisterType<IHttpClientFactory>()
.As<HttpClientFactory>()
.SingleInstance();
Bây giờ bạn sẽ có một triển khai phù hợp và có thể kiểm tra được. Hãy tưởng tượng rằng phương pháp của bạn giống như sau:
public class MyHttpClient
: IMyHttpClient
{
private readonly IHttpClientFactory _httpClientFactory;
public SalesOrderHttpClient(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<string> PostAsync(Uri uri, string content)
{
using (var client = _httpClientFactory.Create())
{
var clientAddress = uri.GetLeftPart(UriPartial.Authority);
client.BaseAddress = new Uri(clientAddress);
var content = new StringContent(content, Encoding.UTF8, "application/json");
var uriAbsolutePath = uri.AbsolutePath;
var response = await client.PostAsync(uriAbsolutePath, content);
var responseJson = response.Content.ReadAsStringAsync().Result;
return responseJson;
}
}
}
Bây giờ là phần thử nghiệm. HttpClient
mở rộng HttpMessageHandler
, đó là trừu tượng. Hãy tạo một "mock" HttpMessageHandler
chấp nhận một đại biểu để khi chúng ta sử dụng mock, chúng ta cũng có thể thiết lập từng hành vi cho mỗi bài kiểm tra.
public class MockHttpMessageHandler
: HttpMessageHandler
{
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _sendAsyncFunc;
public MockHttpMessageHandler(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> sendAsyncFunc)
{
_sendAsyncFunc = sendAsyncFunc;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return await _sendAsyncFunc.Invoke(request, cancellationToken);
}
}
Và bây giờ, với sự trợ giúp của Moq (và FluentAssertions, một thư viện giúp các bài kiểm tra đơn vị dễ đọc hơn), chúng tôi có mọi thứ cần thiết để kiểm tra đơn vị phương pháp PostAsync sử dụng HttpClient
public static class PostAsyncTests
{
public class Given_A_Uri_And_A_JsonMessage_When_Posting_Async
: Given_WhenAsync_Then_Test
{
private SalesOrderHttpClient _sut;
private Uri _uri;
private string _content;
private string _expectedResult;
private string _result;
protected override void Given()
{
_uri = new Uri("http://test.com/api/resources");
_content = "{\"foo\": \"bar\"}";
_expectedResult = "{\"result\": \"ok\"}";
var httpClientFactoryMock = new Mock<IHttpClientFactory>();
var messageHandlerMock =
new MockHttpMessageHandler((request, cancellation) =>
{
var responseMessage =
new HttpResponseMessage(HttpStatusCode.Created)
{
Content = new StringContent("{\"result\": \"ok\"}")
};
var result = Task.FromResult(responseMessage);
return result;
});
var httpClient = new HttpClient(messageHandlerMock);
httpClientFactoryMock
.Setup(x => x.Create())
.Returns(httpClient);
var httpClientFactory = httpClientFactoryMock.Object;
_sut = new SalesOrderHttpClient(httpClientFactory);
}
protected override async Task WhenAsync()
{
_result = await _sut.PostAsync(_uri, _content);
}
[Fact]
public void Then_It_Should_Return_A_Valid_JsonMessage()
{
_result.Should().BeEquivalentTo(_expectedResult);
}
}
}
Rõ ràng là bài kiểm tra này thật ngớ ngẩn, và chúng tôi thực sự đang kiểm tra mô hình của mình. Nhưng bạn hiểu ý rồi đấy. Bạn nên kiểm tra logic có ý nghĩa tùy thuộc vào cách triển khai của bạn, chẳng hạn như ..
- nếu trạng thái mã của phản hồi không phải là 201, nó có nên ném một ngoại lệ không?
- nếu văn bản phản hồi không thể được phân tích cú pháp, điều gì sẽ xảy ra?
- Vân vân.
Mục đích của câu trả lời này là để kiểm tra thứ gì đó sử dụng HttpClient và đây là một cách tốt để làm như vậy.
HttpClient
trong giao diện của bạn là vấn đề nằm ở đâu. Bạn đang buộc khách hàng của mình sử dụngHttpClient
lớp cụ thể. Thay vào đó, bạn nên hiển thị một phần trừu tượng củaHttpClient
.