invoking an async task on fluentassertion - c#

probably a simple one, but cant get it to work;
i've changed the signature one on the methods to Task
On my unit tests i am using fluent assertions.
but cant get this to work:
_catalogVehicleMapper
.Invoking(m => m.MapToCatalogVehiclesAsync(searchResult, filtersInfo, request, default(CancellationToken)))
.Should().Throw<ArgumentException>()
.WithMessage("One of passed arguments has null value in mapping path and can not be mapped");
the MapToCatalogVehiclesAsync is the async method, but i need to await it, but awaiting and asyncing the invocation, doesnt seem to do it..
someone..?

Although Fabio's answer is correct as well, you can also do this:
_catalogVehicleMapper
.Awaiting(m => m.MapToCatalogVehiclesAsync(searchResult, filtersInfo, request, default(CancellationToken)))
.Should().Throw<ArgumentException>()
.WithMessage("One of passed arguments has null value in mapping path and can not be mapped");
And as a side-note, I suggest to always use wildcards in WithMessage. See point 10 from this blog post.

Invoking<T> extensoin method returns Action which with asynchronous method is equivalent to async void - so exceptions will not be awaited.
As workaround you can wrap method under the test to a Func<Task>.
[Fact]
public async Task ThrowException()
{
Func<Task> run =
() => _catalogVehicleMapper.MapToCatalogVehiclesAsync(
searchResult,
filtersInfo,
request,
default(CancellationToken));
run.Should().Throw<ArgumentException>();
}

Related

Task vs Void In Terms Of Controller Disposal

When I use void for the controller's return type, I see that controller is disposed before the action is completed and I get the
"An asynchronous module or handler completed while an asynchronous
operation was still pending."
error.
public async void Test(){
SomeResult result = await GetSomethingAsync();
int a = result.b;
}
But if I use Task instead of void, controller is disposed after the action completes.
Why is this behavior?
In short, because MVC framework cannot await "void" :)
Action of your controller is not the end of the pipeline in fact it is somewhere in the middle. So when you make a return type of your action as "void" the thread that executes the code exists your "Test" method and starts executing what was after the invocation of it. Since it can't await it it assumes that the action has finished executing and that the framework can "kill" your controller.
If you look at the source code you'll find that these two cases are executed in a different way. In particular the one that returns "void" is not awaited. I would recommend having the async/await flow all way, instead of returning "void".
Is there a reason you want to have a return type of "void"?

Async lambda: "a task was cancelled"

Here's the workflow:
Incoming HTTP request to WebApi2 endpoint.
Make synchronous (e.g. not async) call to get some data.
Map response from DB entity to API model.
a. Executes AutoMapper mapping.
b. Includes the following snippet (see below).
c. If operation is "quick", no issue. If operation is "slow", then "a task was cancelled" exception is thrown.
I get lucky in cases when the mapping action is quick. But if I add a Task.Delay(2000), then I get the exception in question. It seems that ASP.NET is not "waiting" for my async lamba to complete?
Here is the body of the mapping expression:
mapping.AfterMap(async (entity, model) => {
var child = await _childRepo.Get(entity.ChildId);
await Task.Delay(2000); // For testing, of course.
if (child != null)
{
// Fill in some properties on model
}
});
Note that this is example code, and I don't intend to make additional DB/repo calls during mapping in "real life".
AfterMap takes an Action, which is a synchronous delegate, not an asynchronous delegate (as I explain on my blog). As such, it does not work as expected with async lambdas.
In this case (since the delegate returns void), the compiler will actually allow an async lambda; however, it will compile to an async void method. (The compiler does this to allow async event handlers). As I describe in my MSDN article on async best practices, you should avoid async void.
One of the reasons to avoid async void is that it is very difficult to detect when an async void method has completed. In fact, (with the exception of WebForm lifetime events), ASP.NET will not even attempt to do so.

Moq Async Callback Fails with multiple parameters

I'm trying to workout if it is something I am doing wrong, or its an issue in moq or NUnit. I am calling a soap endpoint and my service reference is generating both sync and async methods. The call I am making, looks something like:
public async Task DoThisAsync(idnameobject myobj, int id)
{
await ws.DoSomethingAsync(myobj, id);
}
I am setting up my moq to return the callback, so I can interegate the parameters I have called the web service with. My test looks something like:
var callback = new idnameobject();
wsMock
.SetUp(w => w.DoSomethingAsync(It.IsAny<idnameobject>(), It.IsAny<int>())
.Callback<idnameobject, int>((obj, id) => callback = obj);
await myservice.DoThisAsync(myobj, id);
Assert.That(callback.Id, Is.EqualTo(myobj.Id));
At this point, I get a null reference exception when calling my method, which doesn't contain any information in the stack trace. All I have is Exception thrown: 'System.AggregateException' in mscorlib.dll in the output.
The bit that is strange, is that it does not fail if I setup the callback from the synchronous method and change my method to call that.
It also does not fail if I call an async method that only has one parameter.
If anyone has any ideas, please let me know as I don't want to change my method because of our tests, but ideally I want my test to ensure I am calling the web service correctly.
You are mocking ws.DoSomethingAsync() but aren't setting it up to return anything. The DoThisAsync() method will fail because it will try to await null. You can fix this by changing your set up code to
wsMock.SetUp(w => w.DoSomethingAsync(It.IsAny<idnameobject>(), It.IsAny<int>())
.Callback<idnameobject, int>((obj, id) => callback = obj)
.Returns(Task.FromResult(0));
If you are using .NET 4.6 or above you can replace Task.FromResult(0) with Task.CompletedTask.

Correct way to write async / await services in ServiceStack

I m trying to write an async service with ServiceStack and to me it seems that this feature is not really complete.
My questions:
1) How do you pass CancellationTokens in the service methods?
2) What about ConfigureAwait(false) in those methods? For example
public Task<SomeResponse> Get(SomeRequest request)
{
return _manager.ExecuteAsync(request).ConfigureAwait(false);
}
This doesnt compile.
3) Should we be marking such services with the async keyword and return Task to make them awaitable? For example this doesnt work (usage is silly but you get the point)
public async Task<SomeResponse> Get(SomeRequest request)
{
return await _manager.ExecuteAsync(request).ConfigureAwait(false);
}
Should we even be writing async services with ServiceStack? Is there a benefit or the current implementation defeats the purpose?
Thanks
If the methods don't accept cancellation tokens, then they weren't designed to be cancellable, and you can't cancel them.
You're not actually awaiting the task, so there's no await to configure. Just omit the ConfigureAwait since you have no await to configure.
There's no need to mark a method as async if you're not actually going to leverage any of the features of it or accomplish anything with it that isn't already done by just not doing that. It's not breaking anything other than making the code a tiny bit slower, but it's not adding anything either.
You can make an async request as normal using C# async/await, i.e:
var response = await client.GetAsync(requestDto);
Or if you prefer (or cannot use await), you can use Continuations on the returned Task<T>, e.g:
client.GetAsync(new Hello { Name = "World!" })
.Success(r => r => r.Result.Print())
.Error(ex => { throw ex; });
You can cancel an async request with:
client.CancelAsync();
This calls HttpWebRequest.Abort() behind the scenes.

Wraping sync API into Async method

My MVC application consumes a library and some methods of this library call WCF service internally. All methods exposed from this DLL are sync (none of them return Task or Task) and since we don't own that assembly, it is not possible to convert them into Async API.
However, because these methods call WCF service, they are network bound (so ideally they should be async).
I want to use async controller actions in my MVC application to make it more scalable. My question is how to make the entire method pipeline await able when one method is sync in nature.
Async action --> await async method --> await async method 2 --> sync method from library?
Should I use TaskCompletionSource or Task.FromResult to wrap the library method call?
Also, if I use above approach, will my code more scalable than sync version?
My question is how to make the entire method pipeline await able when one method is sync in nature.
You can't. The only solution is to rewrite the dll.
Should I use TaskCompletionSource or Task.FromResult to wrap the library method call?
Neither.
Also, if I use above approach, will my code more scalable than sync version?
No. It will be slightly less scalable.
TaskCompletionSource<T> is a way to create a puppet Task, which can complete at any point you like, and can make it fault at any point you like. This means, this would ideal in your case since you have no control over the API method, which you are trying to consume. Following example will give you a head start.
public class HomeController : Controller
{
public async Task<ActionResult> Index()
{
ViewBag.Message = await ProcessRequest();
return View();
}
//TResult -> can be of any built-in or custom type that you should decide.
Task<TResult> ProcessRequest()
{
// Make a TaskCompletionSource so we can return a puppet Task
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
// Call your sync API method
SyncAPI syncApi = new SyncAPI();
// Call api method and set the result or exception based on the output from the API //method.
tcs.SetResult(TResult);
// Return the puppet Task, which isn't completed yet
return tcs.Task;
}
}

Categories