I'm struggling a bit with one of my tests.
Here is the code I'm testing
public async Task Handle(ReceiveEventsFromSalesForceCommand message, IMessageHandlerContext context)
{
var queryResult = await this.GenerateQueryResultAsync(message).ConfigureAwait(false);
await this.DetermineActionAsync(context, queryResult).ConfigureAwait(false);
}
public async Task<QueryResult<EventStore__c>> GenerateQueryResultAsync(ReceiveEventsFromSalesForceCommand message)
{
QueryResult<EventStore__c> queryResult;
if (string.IsNullOrWhiteSpace(message.NextRecordsUrl))
{
queryResult = await this.forceClient.QueryAsync<EventStore__c>(query).ConfigureAwait(false);
this.log.Info($"AFTER: QueryAllAsync<EventStore>(query), found {queryResult?.TotalSize ?? 0} records");
}
else
{
queryResult = await this.forceClient.QueryContinuationAsync<EventStore__c>(message.NextRecordsUrl).ConfigureAwait(false);
this.log.Info("AFTER: QueryContinuationAsync<EventStore>(query)");
}
return queryResult;
}
And this is my unit test
[TestMethod]
public async Task Test()
{
// arrange
var forceConfig = Substitute.For<ISalesForceCreationHandlerConfig>();
var forceClient = Substitute.For<IForceClient>();
forceClient.QueryAllAsync<EventStore__c>(Arg.Any<string>()).Returns(Task.FromResult(new QueryResult<EventStore__c> { NextRecordsUrl = "Dummy" }));
var messageHandlerContext = Substitute.For<IMessageHandlerContext>();
var handler = new SalesForceBatchCreationHandler(forceClient, null, forceConfig);
// act
await handler.Handle(new ReceiveEventsFromSalesForceCommand(), messageHandlerContext);
// assert
await messageHandlerContext.Received().Send(Arg.Is<ReceiveEventsFromSalesForceCommand>(command => string.IsNullOrWhiteSpace(command.NextRecordsUrl)), Arg.Any<SendOptions>());
await messageHandlerContext.DidNotReceive().SendLocal(Arg.Any<PublishMultipleKlantManagementEnvelopeCreatedEventsCommand>());
}
My problem is that iresult of my GenerateQueryResultAsync method is null and I get a NullReferenceException. How can I make sure the result is not null and avoid the Exception?
Restructure the way you make your async calls. Most probably this {queryResult.TotalSize} is the culprit.
public async Task<QueryResult<EventStore__c>> GenerateQueryResultAsync(ReceiveEventsFromSalesForceCommand message) {
QueryResult<EventStore__c> queryResult;
if (string.IsNullOrWhiteSpace(message.NextRecordsUrl)) {
queryResult = await this.forceClient.QueryAsync<EventStore__c>(query).ConfigureAwait(false);
this.log.Info($"AFTER: QueryAllAsync<EventStore>(query), found {queryResult?.TotalSize ?? 0} records");
} else {
queryResult = await this.forceClient.QueryContinuationAsync<EventStore__c>(message.NextRecordsUrl).ConfigureAwait(false);
this.log.Info("AFTER: QueryContinuationAsync<EventStore>(query)" );
}
return queryResult;
}
Related
I have two methods which return independent data. I assume it's a good idea to run them in parallel.
Without any modifications code look's like:
private async Task<Entity> MethodAsync()
{
...
var model1 = await client.Method1(Id1, cancellationToken);
var model2 = await client.Method2(Id2, cancellationToken);
...
}
Those methods return data as I expecting. Now when I change code all methods return "null". When I inspect Task object in visual studio there are properties Id = 2356, Status = RanToCompletion, Method = "{null}", Result = "".
private async Task<Entity> MethodAsync()
{
var model1Task = client.Method1(Id1, cancellationToken);
var model2Task = client.Method2(Id2, cancellationToken);
var task = Task.WhenAll(new Task[] { model1Task ,model2Task });
await task; //doesn't work
//task.Wait(); //doesn't work
//await Task.WhenAll(new Task[] { model1Task , model2Task }); //doesn't work
//Task.WhenAll(new Task[] { model1Task, model2Task}); //doesn't work
}
Code of client methods almost the same:
public async Task<Model> Method1(Guid Id, CancellationToken cancellationToken)
{
HttpResponseMessage responseMessage = await client.GetAsync($"customEndPoint");
ResponseMessageSingle<Model> response = JsonSerializer.Deserialize<ResponseMessageSingle<Model>>(
await responseMessage.Content.ReadAsStringAsync(cancellationToken),
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return response.result;
}
private class ResponseMessageSingle<T>
{
public bool success { get; set; }
public string message { get; set; }
public T result { get; set; }
}
Also there is AuthorizeInterceptor(DelegatingHandler):
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
some logic...
request.SetBearerToken(TokenResponse.AccessToken);
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
return await Task.FromResult(response);
}
Task.WhenAll does not return a result in your case, because you create an array of Task, which has no result. And since your tasks return different results, you cannot get that in an array nicely.
You should change your code like this to get the results of your methods:
private async Task<Entity> MethodAsync()
{
var model1Task = client.Method1(Id1, cancellationToken);
var model2Task = client.Method2(Id2, cancellationToken);
var task = Task.WhenAll(new Task[] { model1Task ,model2Task });
await task;
var result1 = await model1Task;
var result2 = await model2Task;
...
}
Since both methods will be completed, awaiting them will just immediately get you the results.
I have the code below in a console app. The LookUpUser method gets called and PostAsJsonAsync gets called but breakpoints in the response checking don't get hit afterwards. What am I doing incorrectly in this implementation?
static void Main(string[] args)
{
TestUseCase().GetAwaiter().GetResult();
}
private static async Task TestUseCase()
{
await GetUserGuids();
}
private static async Task GetUserGuids()
{
var userGuids = new List<Guid>();
userGuids.Add(Guid.Parse("7b5cf09c-196c-4e0b-a0e2-0683e4f11213"));
userGuids.Add(Guid.Parse("3a636154-b7fc-4d96-9cd1-d806119ff79f"));
userGuids.ForEach(async x => await LookUpUser(x));
}
private static async Task LookUpUser(Guid adUserGuid)
{
var client = new HttpClientManager().GetHttpClient();
var response = await client.PostAsJsonAsync("api/v1/users/search", new { ADUserGuid = adUserGuid });
if (response.IsSuccessStatusCode)
{
var groups = await response.Content.ReadAsAsync<List<User>>();
}
else //not 200
{
var message = await response.Content.ReadAsStringAsync();
}
}
userGuids.ForEach(async x => await LookUpUser(x));
The delegate in the ForEach is basically a async void (fire and forget)
Consider selecting a collection of Task and then use Task.WhenAll
private static async Task GetUserGuids() {
var userGuids = new List<Guid>();
userGuids.Add(Guid.Parse("7b5cf09c-196c-4e0b-a0e2-0683e4f11213"));
userGuids.Add(Guid.Parse("3a636154-b7fc-4d96-9cd1-d806119ff79f"));
var tasks = userGuids.Select(x => LookUpUser(x)).ToArray();
await Task.WhenAll(tasks);
}
Also assuming HttpClientManager.GetHttpClient() returns a HttpClient there is no need to create multiple instances. on static client should do
static HttpClient client = new HttpClientManager().GetHttpClient();
private static async Task LookUpUser(Guid adUserGuid) {
var response = await client.PostAsJsonAsync("api/v1/users/search", new { ADUserGuid = adUserGuid });
if (response.IsSuccessStatusCode) {
var groups = await response.Content.ReadAsAsync<List<User>>();
} else {
//not 200
var message = await response.Content.ReadAsStringAsync();
}
}
I got it to work by changing the ForEach to:
foreach (var guid in userGuids)
{
await LookUpUserInSecurityApi(guid);
}
Method Under Test
protected override async Task<Name> DoExecuteAsync(NameContext context)
{
context.ThrowIfNull("context");
var request = new Request
{
Id = context.Id,
Principal = context.UserPrincipal,
};
return await this.repository.NameAsync(request, new CancellationToken(), context.ControllerContext.CreateLoggingContext());
}
protected override Name HandleError(NameContext viewContext, Exception exception)
{
if (this.errorSignaller != null)
{
this.errorSignaller.SignalFromCurrentContext(exception);
}
return Name.Unknown;
}
This is implementation of
public abstract class BaseQueryAsync<TInput, TOutput> : IQueryAsync<TInput, TOutput>
{
public async Task<TOutput> ExecuteAsync(TInput context)
{
try
{
return await this.DoExecuteAsync(context);
}
catch (Exception e)
{
return this.HandleError(context, e);
}
}
protected abstract Task<TOutput> DoExecuteAsync(TInput context);
protected virtual TOutput HandleError(TInput viewContext, Exception exception)
{
ExceptionDispatchInfo.Capture(exception).Throw();
}
}
Test Case goes like below
[SetUp]
public void Setup()
{
var httpContext = MvcMockHelpers.MockHttpContext(isAuthenticated: true);
this.controller = new Mock<Controller>();
this.controller.Object.SetMockControllerContext(httpContext.Object);
this.repoMock = new Mock<IRepository>();
this.errorSignaller = new Mock<IErrorSignaller>();
this.query = new NameQuery(this.repoMock.Object, this.errorSignaller.Object);
this.userPrinciple = new Mock<IPrincipal>();
this.context = new NameContext(this.controller.Object.ControllerContext, this.userPrinciple.Object);
}
[Test]
public async Task TestDoExecuteAsyncWhenRepositoryFails()
{
// Arrange
this.repoMock.Setup(
x => x.NameAsync(
It.IsAny<Request>(),
It.IsAny<CancellationToken>(),
It.IsAny<ILoggingContext>())).Throws<Exception>();
// Act
Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);
// Assert
act.ShouldNotThrow();
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
}
To verify the Name Object ,When I use the var result = await act() before the line
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
The this.errorSignaller.Verify fails since it's count is 2 instead of 1. My intention is to check the Name object along with below code.
act.ShouldNotThrow();
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
I knew that if I write a new test case I can easily verify it, but is there any way I can do altogether in this test?
If you want to test the result then use:
Name result = await this.query.ExecuteAsync(this.context);
result.Should().Be(expectefResult);
Make sure to make your test method public async Task
Update
To be able to verify name you would need to set it in the function.
//...code removed for brevity
Name expectedName = Name.Unknown;
Name actualName = null;
// Act
Func<Task> act = async () => {
actualName = await this.query.ExecuteAsync(this.context);
};
// Assert
act.ShouldNotThrow();
actualName
.Should().NotBeNull()
.And.Be(expectedName);
//...rest of code
Original
As already mentioned in the comments, act is a function that returns a Task.
While its implementation is awaited, the function itself still needs to be invoked. And since the function returns a Task it too would need to be awaited.
Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);
var name = await act();
It is the same as having the following function.
async Task<Name> act() {
return await this.query.ExecuteAsync(this.context);
}
You would have to await it the same way
var name = await act();
The only difference being that the former example has the function in a delegate.
Try to avoid mixing blocking calls like .Result with async/await code. This tends to cause deadlocks.
You can try to check it with
await query.ExecuteAsync(this.context);
or
this.query.ExecuteAsync(this.context).GetAwaiter().GetResult();
and in case of Func:
act.Invoke().GetAwaiter().GetResult();
I am writing controllers in Web API 2, against which odata queries will be executed:
[Route("", Name = "GetAccount")]
[HttpGet]
public async Task<IHttpActionResult> GetAccount()
{
var query = Request.RequestUri.PathAndQuery.Split('/')[2]; //this query variable will be something "filter=name eq 'alex'"
var response = _accountService.Get(query);
if (!response.Result.IsSuccessStatusCode)
{
return NotFound();
}
var readAsAsync = response.Result.Content.ReadAsAsync<object>();
if (readAsAsync == null)
{
return NotFound();
}
var result = await readAsAsync;
return Ok(result);
}
How do I inject the Request, specifically as it relates to: var query = Request.RequestUri.PathAndQuery.Split('/')[2]; ?
Here's a very basic test that I've written for this controller:
[TestMethod]
public void GetAccount_Returns_IHttpActionResultTask()
{
var accountsService = new Mock<IAccountService>();
var sut = new AccountsController(accountsService.Object);
Assert.IsInstanceOfType(sut.GetAccount(), typeof(Task<IHttpActionResult>));
}
In order to test with different values for Request.RequestUri...., how do I rewrite my controller to be more testable?
Set the Request property on the ApiCntroller.
[TestMethod]
public async Task GetAccount_Returns_IHttpActionResult() {
//Arrange
var accountsService = new Mock<IAccountService>();
var sut = new AccountsController(accountsService.Object);
sut.Request = new HttpRequestMessage {
RequestUri = new Uri("http://localhost/api/accounts?filter=name")
};
//Act
var result = await sut.GetAccount();
//Assert
Assert.IsInstanceOfType(result, typeof(IHttpActionResult));
}
Also there or some potential blocking issues with the method under test. Mixing async/await with .Result blocking calls can cause deadlocks.
Refactor:
[Route("", Name = "GetAccount")]
[HttpGet]
public async Task<IHttpActionResult> GetAccount() {
var query = Request.RequestUri.PathAndQuery.Split('/')[2]; //this query variable will be something "filter=name eq 'alex'"
var response = await _accountService.Get(query);
if (!response.IsSuccessStatusCode) {
return NotFound();
}
var readAsAsync = response.Content.ReadAsAsync<object>();
if (readAsAsync == null) {
return NotFound();
}
var result = await readAsAsync;
return Ok(result);
}
I'm unit testing one of my async methods but it turns out to be a bit tricky.
In my code I execute two actions witch I try to verify with NSubstitute like so
[TestMethod]
public async Task GivenDebatchingHandler_WhenCommandReceived_EventIsPublishedAndStatusUpdated()
{
// arrange
var forceClient = Substitute.For<IForceClient>();
forceClient.UpdateAsync(EntityNames.EventStore, Arg.Any<string>(), Arg.Any<ExpandoObject>()).Returns(info => Task.FromResult(new SuccessResponse { Success = true }));
var messageHandlerContext = Substitute.For<IMessageHandlerContext>();
var handler = new DebatchingHandler(forceClient);
var #event = new KlantManagementEnvelopeCreatedEvent { Header = new Header { MessageId = "UnitTest" } };
var cmd = new PublishMultipleKlantManagementEnvelopeCreatedEventsCommand { EventsLargePayload = new List<KlantManagementEnvelopeCreatedEvent>(new[] { #event }) };
// act
await handler.Handle(cmd, messageHandlerContext).ConfigureAwait(false);
// assert
await messageHandlerContext.Received().Publish(#event, Arg.Any<PublishOptions>()).ConfigureAwait(false);
await forceClient.Received().UpdateAsync(EntityNames.EventStore, "UnitTest", Arg.Any<ExpandoObject>()).ConfigureAwait(false);
}
The publish is received, but the UpdateAsync is not. This is the code under test:
public async Task Handle(PublishMultipleKlantManagementEnvelopeCreatedEventsCommand message, IMessageHandlerContext context)
{
await Task.WhenAll(message.EventsLargePayload.Select(#event => this.ProcessEvent(#event, context))).ConfigureAwait(false);
}
public async Task ProcessEvent(KlantManagementEnvelopeCreatedEvent envelopeCreatedEvent, IMessageHandlerContext context)
{
await context.Publish(envelopeCreatedEvent).ConfigureAwait(false);
var eventStoreRecord = new EventStore__c { Status__c = EventStoreStatus.Published.ToName() };
await this.forceClient.UpdateAsync(EntityNames.EventStore, envelopeCreatedEvent.Header.MessageId, eventStoreRecord).ConfigureAwait(false);
}
Why is the UpdateAsync call never received?
The test has Arg.Any<ExpandoObject>() in the UpdateAsync assert.
Unless EventStore__c in the method under test derives from ExpandoObject then I don't think that assertion would match.
Try using
await forceClient.Received()
.UpdateAsync(EntityNames.EventStore, "UnitTest", Arg.Any<EventStore__c>())
.ConfigureAwait(false);