You can use Moq to mock it.
Using Moq to do that for you
Now, you probably don’t want to create a new class for each response.
You could write a helper class for tests which you can prime with
whatever response you might want, but that is probably not flexible
enough.
Moq is a popular .NET library that helps mocking objects for testing.
In essence it uses reflection and expressions to dynamically generate
the mocks at runtime during your tests, based on specifications you
declare using a fluent API.
Now, there is a slight issue here, too. As you noticed, the SendAsync
method on the abstract HttpMessageHandler class is protected. The
caveat: Moq can’t auto-implement protected methods as easy as it does
with interfaces or public methods. Reason being, that the fluent API
uses expressions on the mocked Type, and this can’t offer private or
protected members, as you access the class from the outside here. So,
we have to use some more advanced features of Moq to mock out our
protected method here.
Moq, therefore, has an API for that. You do use Moq. Protected; in your
using clauses, and then you can go on on your Moq with the
.Protected() method. This gives you some additional methods on the
Moq, where you can access the protected members using their names.
A complete test of a class using a HttpClient using Moq would look
like this:
// ARRANGE
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
.Protected()
// Setup the PROTECTED method to mock
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
// prepare the expected response of the mocked http call
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':1,'value':'1'}]"),
})
.Verifiable();
// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
BaseAddress = new Uri("http://test.com/"),
};
var subjectUnderTest = new MyTestClass(httpClient);
// ACT
var result = await subjectUnderTest
.GetSomethingRemoteAsync('api/test/whatever');
// ASSERT
result.Should().NotBeNull(); // this is fluent assertions here...
result.Id.Should().Be(1);
// also check the 'http' call was like we expected it
var expectedUri = new Uri("http://test.com/api/test/whatever");
handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(1), // we expected a single external request
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get // we expected a GET request
&& req.RequestUri == expectedUri // to this uri
),
ItExpr.IsAny<CancellationToken>()
);
For unit tests, you don’t mock HttpClient. Instead, you mock
HttpMessageHandler, put that into an HttpClient and have it return
whatever you want that way. If you don’t want to create s specific
derivation of HttpMessageHandler for each test, you can also have Moq
create the mocks for you automatically.
Read the whole article here.