Moq Async Callback Fails with multiple parameters - c#

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.

Related

Mocking IFlurl library methods using NSubstitute is throwing null reference exception

I am using flurl and I am trying to unit test the code below:
public class MyRestClient
{
public async Task<T> Request<T>(IFlurlRequest flurlRequest)
{
try
{
return await flurlRequest
.WithOAuthBearerToken("my-hardcoded-token")
.GetAsync()
.ReceiveJson<T>();
}
catch(HttpFlurlException)
{
throw new MyCustomException();
}
}
}
What I want to test is that if flurlRequest throws an exception of type HttpFlurlException then it will throw MyCustomException. My idea is to moq the flurlrequest and throw an exception. This is how I layed out my test:
var moq = Substitute.For<IFlurlRequest>();
// Problem is with line below:
moq.When(x => x.WithOAuthBearerToken("dummy")).Do(x => { throw new HttpFlurlException(); } );
var myClient = new MyRestClient();
Func<Task> call = async () => { await myClient.Request<object>(moq); };
// FluentAssertions
call.Should().Throw<MyCustomException>();
The code when ran returns a NullReferenceException:
Exception has occurred: CLR/System.NullReferenceException
An exception of type 'System.NullReferenceException' occurred in
Flurl.Http.dll but was not handled in user code: 'Object reference not
set to an instance of an object.'
at Flurl.Http.HeaderExtensions.WithHeader[T](T clientOrRequest, String name, Object value)
So I see its something related to headers... so I tried also mocking that by adding:
var moq = Substitute.For<IFlurlRequest>();
moq.Headers.Returns(new Dictionary<string, object> { {"dummy", new {} };
But I'm constantly getting the same exception. What am I doing wrong?
WithOAuthBearerToken is an extension method, which means it cannot be mocked directly by NSubstitute. When you call When..Do or Returns on an extension method it will run the real code of the extension method. (I recommend adding NSubstitute.Analyzers to your test project to detect these cases.)
Tracing through the extension method implementation at the time of writing, it should be possible to mock the Headers property to throw the required exception, but I think this is dragging in much too much internal knowledge of the library and will result in brittle tests that are tightly coupled to that specific implementation (which is what we are aiming to avoid with mocking!).
I would be very wary of mocking out a third part library in this way, as I outlined in this answer:
The other option is to test this at a different level. I think the friction in testing the current code is that we are trying to substitute for details of [a third-party library], rather than interfaces we've created for partitioning the logical details of our app. Search for "don't mock types you don't own" for more information on why this can be a problem (I've written about it before here).
If possible I suggest trying to use Flurl's built-in testing support instead. That should enable you to fake out the behaviour you need without requiring specific details about Flurl's internal implementation.

invoking an async task on fluentassertion

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>();
}

MVC function returns but waits for async function before sending result

I was trying to explain to a colleague why async void functions are bad and that exceptions won't get caught, but it turns out I might not be understanding them right. We have a piece of code that looks a bit like this:
public ActionResult EmailCandidates(List<string> identityTokens, string subject, string content)
{
// generate list of recipients here
SendEmail(recipients, subject, content); //not awaited
return new AjaxResponse { // AjaxResponse is a wrapper around JSONResponse
IsSuccess = true,
Data = recipients.Select(r=>r.Name)
};
}
private async void SendEmail(List<EmailAddress> recipients, string subject, string content)
{
await Task.Delay(10000); // simulate async send email
throw new Exception(); // manually added
}
What I was expecting, and what I was trying to explain is that if the SendEmail function throws an exception it won't be caught properly because the main function EmailCandidates has already returned to the client. Only that's not what happens. The code above executes in exactly the order I expect:
a call is made from the client to EmailCandidates
SendEmail is called
the email is sent asynchronously (simulated here via an async wait)
control returns to EmailCandidates, and the return is executed
and then it gets kind of weird:
At this point, I expected to get a response to the client but I don't, even though EmailCandidates has returned
10 seconds later the exception is thrown
the exception is caught by the global error handler, and now the client receives a 500 error (unsurprisingly)
So why even though EmailCandidates has returned, does the response not get sent to the client. How does is know to wait for the async SendEmail function?
ASP.NET provides a SynchronizationContext that does keep track of the number of asynchronous operations in flight, and will not send a result until they have all completed. Note that this SynchronizationContext has been removed in ASP.NET Core.
However, you shouldn't be seeing this behavior even on ASP.NET. In the case of a synchronous method calling an async void method, you should see an InvalidOperationException with the message "An asynchronous operation cannot be started at this time.". In the case of an asynchronous method calling an async void method (that doesn't complete before the handler returns), you should see an InvalidOperationException with the message "An asynchronous module or handler completed while an asynchronous operation was still pending."
Since neither of these safety nets are triggering, I suspect that your ASP.NET code is using an old version of ASP.NET. Those safety nets were added in .NET 4.5, which you have to not only have as a build target but you also have to add targetFramework in your web.config.
A new .NET 4.5.2 ASP.NET MVC app with the following code immediately throws an InvalidOperationException, as expected:
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
Test();
return View();
}
private async void Test()
{
await Task.Delay(20000);
throw new Exception("Blah");
}
Your application is working fine, actually is following the default behavior that MVC has. If you put explicitly the exception, then this kind of errors(500) arise when the request is originated from the same machine where the application is on(localhost) if you want to see what the actual user want to see you need to change the value on the webconfig for the By default it is set to RemoteOnly.
If you go to your FilterConfig. You will see this line of code
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
Try to change the value to "On" and you will see that you ended on that error page because the handler error attribute, it is providing post processing logic on an action and when it sees that an exception has escaped from an action it will display an error view instead of the yellow screen of death. The error View is on your application by default inside View/Shared/Error.cshtml
Your Off option
Specifies that custom errors are disabled. This allows display of
detailed errors.
for reference go here: https://msdn.microsoft.com/en-us/library/h0hfz6fc(v=vs.71).aspx
If you put remote only then you will continue seeing the error if you are debugging the website in your localmachine, but if you host the application the final user will not see that error.
Async void methods are kinda different beasts from the "normal" async methods.They have different error handling logic. When an exception is thrown out of an async Task or async Task<T> method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown by an async void method will be raised directly on the SynchronizationContext that was active when the async void method has been called. These exceptions can be observed using UnhandledException event handler.

Moq fails because it expects a return value but doesn't let me provide it

I have
Service.Setup(service => service.AsyncMethod(It.IsAny<Func<Task>>()));
where Service is a Mock and AsyncMethod accepts a Func<Task> and returns a Task. Normally in my code I simply await service.AsyncMethod(..).
When I run this code as a Unit Test in Moq, it fails, giving me an exception invocation failed with mock behavior Strict. Invocation needs to return a value and therefore must have a corresponding setup that provides it.
Okay sure, I need to return a value. Then why can't I do
Service.Setup(service => service.AsyncMethod(It.IsAny<Func<Task>>())).Returns(..)
at all? It tells me that it cannot resolve the symbol 'Returns'. I don't understand what I'm doing wrong...
It can be done such way:
Service.Setup(service => service.AsyncMethod(It.IsAny<Func<Task>>()))
.Returns(Task.CompletedTask);
Solved my own question.
I have an interface Service
interface IService
{
Task asyncMethod(Func<Task> asyncFunc);
}
My Moq is like so
Mock<IService> Service = new Mock<IService>(MockBehavior.Strict);
Service.Setup(service => service.AsyncMethod(It.IsAny<Func<Task>>()));
I cannot specify a .Returns() value for the Setup because it doesn't understand the syntax, 'Returns' symbol not recognized. Changing it to this fixes it.
Service.Setup<Task>(service => service.AsyncMethod(It.IsAny<Func<Task>>()))
.Returns<Func<Task>>(async (asyncFunc) => await asyncFunc.Invoke());

Asynchronous method appears to not be fully executing

I have a process where an incoming user request to our system is being handled. I also want to add some metadata about the request to a database table without impacting the responsiveness of the main process. To achieve this I added a call to an asynchronous method like this:
public static ReturnObject ResponsiveMethod(string ip, string code)
{
// ... some reasonably quick code
IPDetail.InsertAsync(ip); // <-- call to the async method
return new ReturnObject(code);
}
The InsertAsync() method looks like this:
public static void InsertAsync(string ipAddress)
{
Action action = () => IPDetail.Insert(ipAddress);
action.BeginInvoke(aResult => Log.Debug("Completed Insert"), null);
}
And finally, the normally non-asynchronous method called Insert():
private static void Insert(string ipAddress)
{
ApplicationContextHelper.LoadApplicationContext();
var helper = new GeoLocationHelper();
var result = helper.GetDetailsFromIP(ipAddress);
Log.InfoFormat("Succesfully retreived IP data {0}.", ipAddress);
result.Save();
}
In my unit tests the InsertAsync() call works perfectly. Inside the method calls in Insert() there are many operations occuring which are detailed by logging, and all the expected log messages are there, as well as the final result of the result.Save() method.
However, we have a webservice which utilizes something like the ResponsiveMethod() method above and for some reason the asynchronous calls do not complete. All of the logging in the LoadApplicationContext() method gets fired, but after that there is no log activity related to the Insert() and the result.Save() is never getting executed.
Revised summary question to be more concise
My current thinking is that the webservice has completed its task and the thread which called the asynchronous no longer exists. Would this stop the async call from completing?
I've never used BeginInvoke before, but usually where there's a Begin*, you also need the coresponding End*. Please add one, along with correct exception handling.
My first thought is that you may be throwing an exception on your async call in the web service scenario for some reason. I know you've probably pared it down to post it on the web, but is there any "more-or-less unfailable" error handling code in there?
Are you relying on the identity of the caller in the Async method call? The identity may be lost when called from the web service.

Categories