How to unit test MassTransit request response - c#

I am trying to test a method that calls Masstransit Request
public async Task<EventsPingCompleted> SendPing()
{
_logger.LogInformation($"Sending Events Ping");
var message = new EventsPing();
var response = await _bus.Request<EventsPing, EventsPingCompleted>(message).ConfigureAwait(false);
return response.Message;
}
I want to return a specific response as part of my unit test. To do this I created a stubbed consumer because I don't want to unit test the consumer, I am doing that in other tests.
public class StubbedConsumer<T,TRt>: IConsumer<T> where T : class where TRt : class
{
private readonly TRt _response;
public StubbedConsumer( TRt expectedResponse )
{
_response = expectedResponse;
}
public async Task Consume(ConsumeContext<T> context)
{
await context.RespondAsync<TRt>(_response).ConfigureAwait(false);
}
}
My unit test is passing but when I debug it the stubbed consumer is being called in my unit test.
[Fact]
public async Task SendPing_GetResponse()
{
// Arrange
var harness = new InMemoryTestHarness();
var expectedResult = new EventsPingCompleted();
var startEvent = new EventsPing();
// the harness needs a consumer for request to work
harness.Consumer(() => new StubbedConsumer<EventsPing, EventsPingCompleted>(expectedResult));
await harness.Start();
// helper needs to be after harness is started
var helper = new PingHelper(harness.BusControl, _logger.Object);
//connect a request client
var requestClient = await harness.ConnectRequestClient<EventsPing>().ConfigureAwait(false);
// Act
// this configures the response, these 2 are equivalent but without calling get response on the request client the tests time out
// when both are called the StubbedConsumer is called twice
var response = await requestClient.GetResponse<EventsPingCompleted>(startEvent);
var result = await helper.SendPing().ConfigureAwait(false);
try
{
// Assert
Assert.True(harness.Sent.Select<EventsPing>().Any());
Assert.True(harness.Consumed.Select<EventsPingCompleted>().Any());
Assert.Equal(expectedResult.Time, result.Time);
Assert.Equal(expectedResult.Time, response.Message.Time);
}
finally
{
await harness.Stop();
}
}
The first message is dispatched by
var response = await requestClient.GetResponse<EventsPingCompleted>(startEvent);
And then the second happens in my system under test
var result = await helper.SendPing().ConfigureAwait(false);
If I remove the call requestClient.GetResponse() the SUT times out without receiving the response from the stubbed consumer.
Is there a way that I can configure the request client without calling GetResponse so that I'm not sending 2 events?

Related

Masstransit Problem adding Payload to request/response in Unit Testing, alternativly using Pipes in Unit Tests

currently I have the problem that I want to write unit tests for Masstransit in .NET. My request/response consumer has some consumer filters, one of these filters are generating extra data as message payload and attaching this to the request message. In order to test my consumer in a unit test I would like to add the Payload.
Q1) Is it possible to add the payload to the request message
Q2) Alternativly, can I make a mocking filter and set it as consumer filter in the pipeline? (Which sets the payload)
This is my latest attempt:
public class ContactCommandConsumerTest
{
[Fact]
public async Task CreateContactOnUserRequestConsumer_RequestConsumer_IsAttached()
{
var harness = new InMemoryTestHarness { TestTimeout = TimeSpan.FromSeconds(5) };
[...]
var consumer = harness.Consumer<CreateContactOnUserRequestCommandConsumer>(() => new CreateContactOnUserRequestCommandConsumer(loggerConsumer, mapper,kontakteintragRep,machineTime));
var pipe = Pipe.New<PipeContext>(x => x.UseFilter(new MockFilter<PipeContext>()));
// harness.Consumer<CreateContactOnUserRequestCommandConsumer>();
await harness.Start();
try
{
harness.Bus.ConnectConsumePipe<CreateContactOnUserRequestCommandConsumer>(pipe);
var requestClient = await harness.ConnectRequestClient<CreateContactOnUserRequestCommand>();
var response = await requestClient.GetResponse<AcceptedResponse, FaultedResponse>(new
{
EntityInfo = "Vb48cc135-4593-4b96-bb29-2cf136b3d1ee",
});
Assert.True(consumer.Consumed.Select<CreateContactOnUserRequestCommand>().Any());
Assert.True(harness.Sent.Select<FaultedResponse>().Any());
}
finally
{
await harness.Stop();
}
}
}
internal class MockFilter<T> : IFilter<T> where T: class, PipeContext
{
public void Probe(ProbeContext context)
{
context.CreateFilterScope("mock");
}
public async Task Send(T context, IPipe<T> next)
{
context.GetOrAddPayload(() => new ContextUserPayload() { ContextUser = new Guid("dc6e091f-669e-45b3-9dd6-a36316f70527") });
await next.Send(context);
}
}
I tried to build a pipe and add it to "harness.bus.ConnectConsumerPipe". But the mock filter is never called ???
You use use the OnConfigureInMemoryBus event on the InMemoryTestHarness to add your filter to the bus endpoint.
Similar to:
harness.OnConfigureInMemoryBus += configurator =>
{
configurator.UseFilter(...);
}
To add a filter to the request, use:
using RequestHandle<TRequest> requestHandle = requestClient.Create(message, cancellationToken);
requestHandle.UseFilter(...);
return await requestHandle.GetResponse<TResponse>().ConfigureAwait(false);

Mocked HttpClientFactory returns null when creating client

I am trying to unit test a service that uses the IHttpClientFactory with Nunit and NSubstitute for mocking.
The service I want to test looks like this
public class Movies : IMovies
{
private readonly IHttpClientFactory _httpClientFactory;
public Movies(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<MovieCollection> GetPopularMovies(int PageNumber = 1)
{
// Get an instance of HttpClient from the factpry that we registered
// in Startup.cs
var client = _httpClientFactory.CreateClient("Movie Api");
// Call the API & wait for response.
// If the API call fails, call it again according to the re-try policy
// specified in Startup.cs
var result =
await client.GetAsync($"movie/popular?api_key=<the_api_key>language=en-US&page={PageNumber}");
if (result.IsSuccessStatusCode)
{
// Read all of the response and deserialise it into an instace of
var content = await result.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<MovieCollection>(content);
}
return null;
}
}
When I run the test I get an error that says
System.NullReferenceException : Object reference not set to an instance of an object.
at MovieApi.Services.Movies.GetPopularMovies(Int...
Here is the test I am running.The error occurs only when I put the keyword await in the line
var result = await service.GetPopularMovies(1);
Check the Test code below:
[Test]
public async Task GetPopular_WhenCalled_ReturnOK()
{
//arrange
var moviecollection = new MovieCollection();
var httpClientFactoryMock = Substitute.For<IHttpClientFactory>();
var fakeHttpMessageHandler = new FakeHttpMessageHandler(new HttpResponseMessage() {
StatusCode = HttpStatusCode.OK,
Content = new StringContent(JsonConvert.SerializeObject(moviecollection), Encoding.UTF8, "application/json")
});
var fakeHttpClient = new HttpClient(fakeHttpMessageHandler);
httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);
// Act
var service = new Movies(httpClientFactoryMock);
var result = await service.GetPopularMovies(1);
//assert
Assert.IsNotNull(result);
}
The subject method under test calls
var client = _httpClientFactory.CreateClient("Movie Api");
but you configure the mock to return when CreateClient() is invoked.
httpClientFactoryMock.CreateClient().Returns(fakeHttpClient);
which means that when testing and CreateClient("Movie Api") is invoked the mock wont know what to do and thus returns null, causing the next call to throw NRE
Setup the mock to behave as expected when the system under test is invoked.
//...
httpClientFactoryMock.CreateClient("Movie Api").Returns(fakeHttpClient);
//...

How do I test a public function that has private method dependency?

I'm trying to test a public method (Method A) that consumes an HttpClient to access an external API. This public method calls a private method (Method B) of the same class to get an Access Token that is required by the HttpClient of Method A to send the request. The problem I am having is that I am creating a mock of the HttpClientFactory interface in order to test the response of the Method A, but in order for Method B get the token it needs its own instance of HttpClient. Therefore, the mock instance created in the Test method will be used by the Method B as well, and it will fail trying to get the Access Token. The following code makes the scenario more clear.
Method to be tested (Method A):
public async Task<HttpResponseMessage> SendAsync(string requestUri, string siteName, int accountId)
{
try
{
var accessToken = await GetTokenAsync(siteName, accountId);
if (accessToken == null)
throw new ArgumentNullException("Error Sending request - Could not find an access token");
var request = new HttpRequestMessage(HttpMethod.Get, $"{accessToken.Api}{requestUri}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Accesstoken);
var httpClient = _httpClientFactory.CreateClient();
return await httpClient.SendAsync(request);
}
catch (Exception e)
{
throw new Exception("Error Sending request.", e);
}
}
Test Method:
[Fact]
public async Task ShouldReturnHttpResponseMessage_OnSendAsync()
{
//_jaClientMock.Setup(x => x.GetTokenAsync(It.IsAny<string>(), It.IsAny<int>())).Verifiable();
_appSettingsMock.Setup(x => x.Value)
.Returns(GetValidFakeAppSettings());
HttpResponseMessage expectedResponse = GetListOfContacts(HttpStatusCode.OK, false);
_httpClientFactoryMock.Setup(x => x.CreateClient())
.Returns(GetMockedHttpClient(expectedResponse));
var response = await _jaClient.SendAsync("someurl", "siteName", 1000);
response.IsSuccessStatusCode.ShouldBeTrue();
}
The private Method (Method B):
private async Task<AccessToken> GetTokenAsync(string siteName, int accountId)
{
try
{
if (_cache.TryGetValue(GetCacheKeyForToken(siteName, accountId), out AccessToken value))
return value;
....
var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.SendAsync(request);
if (response.IsSuccessStatusCode)
{
accessToken = await response.Content.ReadAsAsync<AccessToken>();
}
.....
return accessToken;
}
catch (Exception e)
{
throw new Exception("Error Getting an Access Token.", e);
}
}
Any idea How I can test Method A?
There ain't no such thing as a free lunch - if one wants to unit-test some code with external dependencies, then each and every of those external dependencies has to be mocked.
Or one can go one step up the test pyramid to integration tests (though it is not our case, probably).
So, you could:
Either mock the Token response in the _httpClientFactory the same way you mock it for the SendAsync ( ..._httpClientFactoryMock.Setup(x => x.CreateClient()).Returns(GetMockedHttpClient(expectedResponse));...)
Or reorganize code in such a manner that tokens are not retrieved directly from API - create some single-method ITokenProvider interface that will be a bit easier to mock.
public interface ITokenProvider
{
public async Task<AccessToken> GetTokenAsync(string siteName, int accountId);
}
...
public async Task<HttpResponseMessage> SendAsync(string requestUri, string siteName, int accountId)
{
try
{
var accessToken = await _tokenProvider.GetTokenAsync(siteName, accountId);
...
[Fact]
public async Task ShouldReturnHttpResponseMessage_OnSendAsync()
{
var tokenProviderMock = new Mock<ITokenProvider>()
.Setup(o => o.GetTokenAsync("siteName", 1000))
.Returns(Constants.AllowedToken);
_jaClient = new JaClient(tokenProviderMock.Object);...

C# Mock IHttpclient & CreateClient

I have a function that I want to x-unit test, but it seems that I have to mock the CreateClient function? Whenever I debug it during testing it seems that the var client is equals to null. I am injecting the dependencies properly, I am sure of that. What I want to know is how to mock the CreateClient.
here is that function:
public async Task CreateMessageHistoryAsync(Message message)
{
//This seems to be giving a null value
var client = this.clientFactory.CreateClient(NamedHttpClients.COUCHDB);
var formatter = new JsonMediaTypeFormatter();
formatter.SerializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
Guid id = Guid.NewGuid();
var response = await client.PutAsync(id.ToString(), message, formatter);
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(await response.Content.ReadAsStringAsync());
}
}
here is the unit test, I am mocking the IHttpClient in a separate class and I am using that class.
[Collection("MockStateCollection")]
public class CreateMessageHistory
{
private readonly MockStateFixture mockStateFixture;
public CreateMessageHistory(MockStateFixture mockStateFixture)
{
this.mockStateFixture = mockStateFixture;
}
[Fact]
public async Task Should_NotThrowHttpRequestException_When_AMessageHistoryIsCreated()
{
var recipients = MockMessage.GetRecipients("Acc", "Site 1", "Site 2", "Site 3");
var message = MockMessage.GetMessage(recipients);
mockStateFixture
.MockMessageHistoryService
.Setup(service => service.CreateMessageHistoryAsync(message));
var messageHistoryService = new MessageHistoryService(
mockStateFixture.MockIHttpClientFactory.Object);
mockStateFixture.MockIHttpClientFactory.Object.CreateClient("CouchDB");
var task = messageHistoryService.CreateMessageHistoryAsync(message);
var type = task.GetType();
Assert.True(type.GetGenericArguments()[0].Name == "VoidTaskResult");
Assert.True(type.BaseType == typeof(Task));
await task;
//await Assert.IsType<Task>(messageHistoryService.CreateMessageHistoryAsync(message));
// await Assert.ThrowsAsync<HttpRequestException>(() => messageHistoryService.CreateMessageHistoryAsync(message));
}
}
it seems to me that I also need to mock the CreateClient class is it?
You should inject a mocked object for ClientFactory for which you have setup the CreateClient method.
// create the mock client
var httpClient = new Mock<IHttpClient>();
// setup method call for client
httpClient.Setup(x=>x.PutAsync(It.IsAny<string>()
, It.IsAny<Message>(),
, It.IsAny< JsonMediaTypeFormatter>())
.Returns(Task.FromResult(new HttpResponseMessage { StatusCode = StatusCode.OK}));
// create the mock client factory mock
var httpClientFactoryMock = new Mock<IHttpClientFactory>();
// setup the method call
httpClientFactoryMock.Setup(x=>x.CreateClient(NamedHttpClients.COUCHDB))
.Returns(httpClient);
Then you have to pass the httpClientFactoryMock.Object to the constructor:
var messageHistoryService = new MessageHistoryService(httpClientFactoryMock.Object);
Update
In order to unit test HttpClient since it hasn't any interface you should wrap it in way as it is described here.
Specifically we have to arrange the http client as below:
// Mock the handler
var handlerMock = new Mock<HttpMessageHandler>(MockBehavior.Strict);
handlerMock.Protected()
// Setup the PROTECTED method to mock
.Setup<Task<HttpResponseMessage>>("PutAsync",
ItExpr.IsAny<String>(),
ItExpr.IsAny<Message>()
ItExpr.IsAny<MediaTypeFormatter>())
// prepare the expected response of the mocked http call
.ReturnsAsync(new HttpResponseMessage()
{
StatusCode = HttpStatusCode.OK
})
.Verifiable();
// use real http client with mocked handler here
var httpClient = new HttpClient(handlerMock.Object)
{
BaseAddress = new Uri("http://test.com/"),
};
Now we should return the above httpClient when CreateClient is called.
// create the mock client factory mock
var httpClientFactoryMock = new Mock<IHttpClientFactory>();
// setup the method call
httpClientFactoryMock.Setup(x=>x.CreateClient(NamedHttpClients.COUCHDB))
.Returns(httpClient);

Parallel Invoke with AwaitAsync in WebApi blocking

I've a webApi operation which executes 2 operations in || which internally invokes HttpClient sendAsync. If I apply debuggers and execute call, it works and returns. If I remove debuggers, both the async calls still work (checked in Fiddler) but caller of WebApi operation doesn't gets any response (using AdvanceRest chrome plugin). From the other threads, possibly I'm not using async/await correctly and related to ASP.NET synchronizationContext
//**WEB API Controller***
class SomeController
{
public HttpResponseMessage Get()
{
Client someClient = new Client();
aResponse = new aResponse();
bResponse = new bResponse();
Parallel.Invoke(
() => {aResponse = someClient.a()},
() => {bResponse = someClient.b()});
var response = {a=aResponse, b=bResponse};
return Response.Create(OK, response}
}
class SomeClient
{
AResponse a()
{
var clientResponse = ClientMgr.Execute("url");
return new AResponse {HttpClientResponse = clientResponse.Result}
}
BResponse b()
{
var clientResponse = ClientMgr.Execute("url");
return new BResponse {HttpClientResponse = clientResponse.Result}
}
}
//Utility CLASS
public class ClientMgr
{
public static async Task<HttpResponseMessage> Execute(string url)
{
request = new HttpRequestMessage();
//....request fill
HttpClient client = new HttpClient();
var response = await client.SendAsync(request);
client.dispose();
return response;
}
}
public class AResponse
{
HttpResponseMessage HttpClientResponse {get;set;}
// Some other properties....
}
Why does operation returns response when I'm using breakpoints but as I soon as I remove them, it doesn't returns response?
Your problem (other than the fact that the code you posted doesn't compile) is that while you debug, the async operations actually complete. When you don't debug, they don't, and it returns a Task<YourResponse>, not the actual result of the Task.
In order for this to work, mark your method as async and use Task.WhenAll to asynchronously wait on both tasks:
[HttpGet]
public async Task<HttpResponseMessage> GetAsync()
{
Client someClient = new Client();
var aTask = someClient.AAsync();
var bTask = someClient.BAsync();
await Task.WhenAll(aTask, bTask);
var response = { a = aTask.Result, b = bTask.Result };
return Response.Create(OK, response}
}
Side note - You don't need to use Paralle.Invoke when you have IO bound operations. Those are redundant threads which will be blocked waiting for the IO's completion.

Categories

Resources