How to mock httpclient - c#

I am new in TDD can you please write test case using moq for following code -
public async Task<Model> GetAssetDeliveryRecordForId(string id)
{
var response = await client.GetAsync($"api/getdata?id={id}");
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsAsync<Model>();
return result;
}
Thanks in advance.

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.

Related

Mocking Authentication in .Net with IRequestExecutor

Trying to write some integration tests for the first time in .NET - specifically for HotChocolate.
I’ve got had tests working with the WebApplicationFactory, the final part left is trying to now mock the authentication. I tried to set it up based on Mock Authentication but I’m querying the data with IRequestExecutor and finding the de-facto way of setting it up doesn't actually fire:
[Fact]
public async Task GetJokes()
{
var query =
#"query Jokes {
jokes(jokeLength: SMALL) {
nodes {
id
body
}
}
}";
var request = QueryRequestBuilder.New().SetQuery(query).Create();
var executor = await _factory.Services.GetRequestExecutorAsync();
var result = await executor.ExecuteAsync(request);
(await result.ToJsonAsync()).MatchSnapshot();
}
When I debug this I never hit HandleAuthenticateAsync in my AuthHandler’s (setup from the Mock Authentication linked above), so the claims are never added - so looks like those only run for the httpClient requests - is there a way to configure the IRequestExecutor to be authenticated too?
You can add the ClaimsPrincipal directly as property to the QueryRequestBuilder.
var request = QueryRequestBuilder
.New()
.SetQuery(query)
.AddProperty(nameof(ClaimsPrincipal), new ClaimsPrincipal(new ClaimsIdentity("test")))
.Create();

How to moq HttpClientExtensions method "PostAsJsonAsync"?

I need to write the unit test case around PostAsJsonAsync, which is extension method in HttpClientExtensions. I don't find any easy way to moq it.
Can someone help me in this.
Two ways I can think of:
Use a framework like Moles: https://www.microsoft.com/en-us/research/project/moles-isolation-framework-for-net/ This way you can replace extensions or any other methods with your own, returning the value you want
This is my preferred way. In such cases, wrap the service inside a proxy Interface. You might find it called adapter pattern elsewhere but in my opinion you are just abstracting the actions and proxying the data.
So create the IHttpClientProxy and the corresponding concrete implementation that will use any extention you like. Pass the IHttpClientProxy to your class and mock it as you like.
As mentioned in the comments, there is no way the mocking frameworks to mock static methods this way. Frameworks like Moq, only mock virtual or abstract methods (interface methods are inherently abstract) to guide towards better design.
The issue for me was that I was not understanding that PostAsJsonAsync was actually a convenience method that ultimately calls SendAsync on the handler. So many of the answers you find relating to mocking HTTP client are accurate. The gist is that you are indeed mocking the HttpMessageHandler, and using a concrete HttpClient. The missing piece was that you still need to perform setup and verification on 'SendAsync', not PostAsJsonAsync. Here is the code that worked for me to test PostAsJsonAsync, because SendAsync gets called on the handler:
// Arrange
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("[{'id':1,'value':'1'}]"),
})
.Verifiable();
// new up a real HttpClient, passing in the mocked handler
var httpClient = new HttpClient(handlerMock.Object)
{
BaseAddress = new Uri("http://example.com")
};
// replace with an instance of your actual concrete client
var yourClient = new YourClientHere(httpClient);
// Act
// perform the action on yourClient that makes the PostAsJsonAsync call
// Assert
handlerMock.Protected().Verify(
"SendAsync",
Times.Exactly(accessRights.Count),
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
);
This article helped me get everything set up correctly: https://gingter.org/2018/07/26/how-to-mock-httpclient-in-your-net-c-unit-tests/
I am using NSubstitute for mocking this extension method on HttpClient and it seems seems to work fine without the use of Moles or an adapter.
public class ApiClientTests
{
private HttpClient _client;
private string _url;
private ModelDto _testModel;
public void ApiClientTests()
{
_client = Substitute.For<HttpClient>();
_client.BaseAddress = new Uri("http://fakeUrl/api/")
_url = "Models/";
_testModel = new ModelDto
{
Id = 1,
Name = "Model Name",
Description = "Model Description",
Outputs = new Dictionary<string, ModelOutputDto>(),
Parameters = new Dictionary<string, ModelParamDto>(),
Active = true
};
}
[Fact]
public async Task CreateItemAsync_ValidResponseCode_ReturnsNewResourceUri()
{
// Arrange
var returnUri = $"{_client.BaseAddress}{_url}";
var returnThis = new HttpResponseMessage(System.Net.HttpStatusCode.Created);
returnThis.Headers.Location = new Uri(returnUri);
_client.PostAsJsonAsync(_url, _testModel).ReturnsForAnyArgs(Task.FromResult(returnThis));
var apiClient = new ApiClient<ModelDto>(_client);
// Act
var result = await apiClient.CreateItemAsync(_testModel, _url);
// Assert
await _client.ReceivedWithAnyArgs().PostAsJsonAsync(_url, _testModel);
result.AbsoluteUri.Should().BeEquivalentTo(returnUri);
}

How to test in xunit complex method

How can I test in asp.net core 2.0 following method which exists in separate project than my test project? for example like this:
public partial class LoanRequestServiceController : BaseServiceController
{
public ServiceDTO<AP_CBO> AddCBO(AP_CBO cbo)
{
ServiceDTO<AP_CBO> dto = new ServiceDTO<AP_CBO>();
try
{
using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.Snapshot }))
{
cbo.ID_CBO = 333;
dto.Data = cbo;
scope.Complete();
}
}
catch (Exception ex)
{
dto.Error = new ServiceError(ex);
Globals.Logger.Error(ex);
}
finally
{
//Globals.CastleComponentsContainer.Release(LoanRequestDAL);
}
return dto;
}
}
I tested some "light" methods such as if service method returns SucessCode and it works.
Here is my test class:
[Theory]
[InlineData("/Sample/AddCBO")]
public async Task Test_AddCBO(string url)
{
//Arrange
var client = _factory.CreateClient();
//Act
var response = await client.GetAsync(url);
//Assert
response.EnsureSuccessStatusCode();
//Compare two dto objects AP_CBO
//object expected = new AP_CBO { properties... }
// object responseObject = response.Content...
//Assert.Equal(expected, responseObject);
}
I don't know how to test an object with muliple properties.
Maybe I need to use Moq? Theoretically, this method would be go to the DAL (DatabaseAccess Layer) and return from database packed object and returns to the api, or in my case back into test.
First off, you have to decide which level of tests you want to write.
If you're writing a Unit test, you should mock any and all external integrations (in your case I can identify HTTP request -> Controller and Controller -> Database). This is the foundation of your functional testing. So if you're writing unit tests, yes, you should use a mocking framework such as NSubstitute or Moq (and only test your method's behavior by calling it).
The test sample you posted looks to me like an integration test since you're including the integration HTTP request -> Controller. In this case I would seed the database with data (if relevant) and actually call your API endpoint (as you're already doing).
To check the content (DTO) of the response in ASP.Net Core you have to do the following:
// ...
var response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
var content = await httpResponseMessage.Content.ReadAsStringAsync();
var serviceDto = JsonConvert.DeserializeObject<ServiceDTO<AP_CBO>>(content); // Only for Json
// Validate serviceDto
It is pretty long topic for detailed explanation here ; i think it will be better if you follow a sample and read the details.
I assume that you are going to write unit test; for unit test i can recommend this tutorial that may help you . check this please

How to instantiate ResourceResponse<Document>

I am trying to mock a call that returns ResourceResponse<Document>, but I am not able to instantiate that type. Is there a factory class that can instantiate it or some other way to do so?
EDIT
var response = new ResourceResponse<Document>();
The type 'Microsoft.Azure.Documents.Client.ResourceResponse' has no constructors defined
The latest stable version of Microsoft.Azure.DocumentDB (1.10.0) atm added 2 constructors for mocking purposes.
https://msdn.microsoft.com/en-us/library/azure/dn799209.aspx#Anchor_2
Edit
Using Moq you could do something like this:
Mock<IDocumentClient> documentClient = new Mock<IDocumentClient>();
documentClient
.Setup(dc => dc.ReplaceDocumentAsync(UriFactory.CreateDocumentUri("database", "collection", "id"), object, null) // last parameter are RequestOptions, these are null by default
.Returns(Task.FromResult(new ResourceResponse<Document>()));
This way I can check if the method on my documentClient is being called, if you want to influence what is returned in the document, you have to create a document, and following that a ResourceResponse of that document. Something like:
var document = new Document();
document.LoadFrom(jsonReader); // the json reader should contain the json of the document you want to return
Mock<IDocumentClient> documentClient = new Mock<IDocumentClient>();
documentClient
.Setup(dc => dc.ReplaceDocumentAsync(UriFactory.CreateDocumentUri("database", "collection", "id"), object, null) // last parameter are RequestOptions, these are null by default
.Returns(Task.FromResult(new ResourceResponse<Document>(document)));
The ResourceResponse class has the constructor that contains the DocumentServiceResponse parameter marked as internal.
This is bad because even though you can create a ResourceReponse object from your DTO class you cannot set things like RUs consumed, response code and pretty much anything else because they are all coming from the ResourceResponseBase which also has the DocumentServiceResponse marked as internal.
Please find code at below link
how-to-mock-or-instantiate
Kinda late but with Microsoft.Azure.DocumentDB.Core 2.4.2 there's a DocumentClient constructor that accepts an HttpMessageHandler as a parameter. It's ugly (so much so it deserves a downvote) but you can use this to inject responses with status codes. As far as I can tell nothing's leaving my box when I do this.
I also had to create a test-only constructor since I would never want to use this during running code. But for anyone still interested until a better library comes out here are the relevant parts.
Create your client
var client = new DocumentClient(host, authText, handler);
In your tests you can use a mocked handler like this:
public static Mock<HttpMessageHandler> CreateHttpMessageHandler(List<HttpResponseMessage> responses)
{
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock.Protected()
.Setup<Task<HttpResponseMessage>>(
nameof(HttpClient.SendAsync),
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(() => {
var response = responses[0];
responses.RemoveAt(0);
return response;
})
.Verifiable();
return handlerMock;
}
I created this response factory simulator
private HttpResponseMessage Respond(string text)
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(text),
};
return response;
}
and set up the handler with something like this:
var responses = new List<HttpResponseMessage> {
Respond("{ ... }"),
Respond("{ ... }"),
Respond("{ ... }"),
Respond("{ ... }"),
};
Replace the ... with your Fiddler (or similar) trace output from an actual call to your CosmosDB. FWIW I had to supply 2 copies of my actual payload as the final 2 responses. This is obviously AS-IS and SUBJECT TO CHANGE but It worked for me. Hope it helps you and looking forward to a modern library designed to interfaces with SOLID principles.

How do I make a unit test to test a method that checks request headers?

I am very, very new to unit testing and am trying to write a test for a pretty simple method:
public class myClass : RequireHttpsAttribute
{
public override void OnAuthorization(AuthoizationContext filterContext)
{
var request = filterContext.HttpContext.Request;
var header = Convert.ToBoolean(request.Headers["Special-Header-Name"]);
if (!(header || request.IsSecureConnection))
{
HandleNonHttpsRequest(filterContext);
}
}
}
This method, which inherits from the RequireHttpsAttribute, checks if a certain header is present from a page, if it's missing or false, and the page is not secure, then it will call HandleNonHttpsRequest, otherwise it does nothing.
We are using Moq and Nunit for testing. I have found some resources to help build a fakeHttpContext with Moq, but honestly I'm not sure how to use it or where to go within my unit tests to ensure that fake HttpContexts are or are not causing the HandleNonHttpsRequest method to call.
I really appreciate any guidance with this issue.
// arrange
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var headers = new NameValueCollection
{
{ "Special-Header-Name", "false" }
};
request.Setup(x => x.Headers).Returns(headers);
request.Setup(x => x.HttpMethod).Returns("GET");
request.Setup(x => x.Url).Returns(new Uri("http://www.example.com"));
request.Setup(x => x.RawUrl).Returns("/home/index");
context.Setup(x => x.Request).Returns(request.Object);
var controller = new Mock<ControllerBase>();
var actionDescriptor = new Mock<ActionDescriptor>();
var controllerContext = new ControllerContext(context.Object, new RouteData(), controller.Object);
var filterContext = new AuthorizationContext(controllerContext, actionDescriptor.Object);
var sut = new myClass();
// act
sut.OnAuthorization(filterContext);
// assert
Assert.IsInstanceOfType(filterContext.Result, typeof(RedirectResult));
var redirectResult = (RedirectResult)filterContext.Result;
Assert.AreEqual("https://www.example.com/home/index", redirectResult.Url);
Yes, I'd use Moq and create a Mock<AuthorizationContext>. You'll need a series of mock objects to setup the fake request, most notably to specify a NameValueCollection of fake headers.
var request = new Mock<HttpRequestBase>();
request.SetupGet(c => c.Headers).Return(new NameValueCollection{ /* initialize values here */});
request.SetupGet(c => c.IsSecureConnection).Return(/*specify true or false depending on your test */);
var httpContext = new Mock<HttpContextBase>();
httpContext.SetupGet(c => c.Request).Return(request.Object);
var filterContext = new Mock<AuthorizationContext>();
filterContext.SetupGet(c => c.HttpContext).Return(httpContext.Object);
var myclass = new myClass();
myClass.OnAuthorization(filterContext.Object);
(sorry if syntax or usage is slightly off; doing this from the top of my head)
You may need to go in and mock any additional members on filterContext that HandleNonHttpsRequest invokes. I have two recommendations for going about this, as it can sometimes be a hassle if the method you are testing is doing lots of complex stuff on filterContext: 1) check visually and, if it's straight forward enough, mock all the invoked pieces 2) create the myClass.OnAuthorizationRequest, but don't implement any code yet other than the call to HandleNonHttpsRequest. Keep running the test and fixing missing/incorrectly mocked members until the test passes. Then implement your actual logic for OnAuthorizationRequest, testing and fixing (rinse repeat) until it passes.
I encountered an issue with the accepted solution using ASP.NET MVC 4. To resolve it I mocked the http context Items attribute otherwise the sut.OnAuthorization was causing an object is undefined exception:
MockHttpContext.Setup(x => x.Items)
.Returns(new System.Collections.Generic.Dictionary<object, object>());

Categories

Resources