How to unit test a synchronous method calling asynchronous method? - unit-testing

What is the correct way to write a unit test for a synchronous method calling async methods.
Right now my unit test are passing, but when I try to open the page, it never returns.
Why isn't my unit test failing? How can I make it fail?
I replicated my problem with this simple code:
My passing test:
[TestMethod]
public void DoSomeWork_WhenWeDoSomeWork_ShouldReturnDone()
{
var service = new SyncService();
const string expected = "Done";
var actual = service.DoSomeWork();
Assert.AreEqual(expected, actual);
}
My view that never returns:
public ActionResult Index()
{
var syncService = new SyncService();
return View((object)syncService.DoSomeWork());
}
My service that never returns to view:
public class SyncService
{
public string DoSomeWork()
{
return SomeWork().GetAwaiter().GetResult();
}
private async Task<string> SomeWork()
{
var task1 = Task.Delay(1000);
var task2 = Task.Delay(1000);
await Task.WhenAll(task1, task2);
return "Done";
}
}

I don't think I can help you with this specific example, but I think a good general strategy is to write two tests. One to test if the synchronous method passes the correct data and an other to test if the asynchronous method works properly.
I mostly work in JavaScript and that general approach works for me. Also you can check the documentation of your testing frameworks, maybe it provides some methods for this.

First, don't block on async code (link to my blog). By blocking on async code, you're actually causing a deadlock. This deadlock does not happen in your unit test because unit tests run in a thread pool context, not an ASP.NET context (link to my blog).
There are good reasons for not having synchronous wrappers for asynchronous methods. So I recommend getting rid of DoSomeWork completely, leaving only SomeWork (renamed to SomeWorkAsync).
To solve your problem, you should use asynchronous controller actions.

Related

How can I wait for a future to finish during a test if it wasn't called from the test directly?

I'm trying to write a test for a method that makes a call to an API using Dio. The Dio response has been mocked using http_mock_adapter. My problem is that I need to wait for the API call to finish before continuing with the test, and I can't simply use await since the method I'm testing isn't asynchronous. Is there a way to wait for a future that wasn't called from the test?
Below is an example of what I'm talking about:
String apiResult = 'foo';
void methodToTest(){
apiCall().then((value) => apiResult = value);
}
test('methodToTest works', () {
expect(apiResult, equals('foo'));
methodToTest();
// I need to wait for apiCall to finish here.
expect(apiResult, equals('bar'));
});
Previously, I have been able to use Future.delayed(Duration.zero) when I have had situations like this, but it has always seemed like a workaround, and now it doesn't work.
the method I'm testing isn't asynchronous
Congratulations, your tests found a bug.
this is your method after fixing the bug:
Future<void> methodToTest() async {
apiResult = await apiCall();
}

Unit testing method with async

I am writing a unit test for a method using Moq framework.
The method calls the method Task<HttpResponseMessage> SendAsync(HttpRequestMessage request) of System.Net.Http with await.
When I execute my test case the method does not return anything. Certainly, I am doing something wrong. Please help me to find it.
Please note that this call is not loosely coupled.
I did check the other answers like Unit testing async method - test never completes which have it as loosely coupled and could be mocked.
Edit 1:
The method similar to which I am trying to unit test
class Class1
{
public async Task<string> Method(string url)
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get,url);
HttpResponseMessage response = await new HttpClient().SendAsync(request);
return System.Convert.ToString( response);
}
}
Are you trying to mock the HttpClient? If so this isn't the recommended approach; instead of mocking HttpClient instantiate a real HttpClient, passing a mocked HttpMessageHandler into the constructor.
Source: https://gingter.org/2018/07/26/how-to-mock-httpclient-in-your-net-c-unit-tests/
Edit: to read the contents of a HTTP response you need to call the response.Content.ReadAsStringAsync() method instead of .ToString().

What is a good practice to write unit-test on .net core Ihostedservice?

I have a background task initiated in .net core 2.0 startup, inherits from backgroundservice, implementing StartAsync, StopAsync and ExecuteAsync. This task is to update some data in database table periodically based on some business logic.
While I can run the backgroundtask as an application and test using logs, db check and with the help of other tools, can the unit-testing is necessary for testing the backgroundtask? If so how to register the task as a service with dependencies and trigger the start and stop methods to assert the actual vs expected? Appreciate some basic sample unit-test method on testing timer based .net core ihostedservice backgroundtask.
Here is my basic test start just for sample, but not completed yet. Having said that, this is just a thought but not the exact working test. Here is what need some help from the community. Can also add some more asserts i.e. Assert.Verify()?
[Fact]
public async void Run_background_task_success()
{
//Arrange
IServiceCollection services = new ServiceCollection();
services.AddHostedService<BackgroundManagerTask>();
var serviceProvider = services.BuildServiceProvider();
var service = serviceProvider.GetService<IHostedService>() as BackgroundManagerTask;
var isExecuted = false;
if(await service.StartAsync(CancellationToken.None))
{
isExecuted = true;
}
await Task.Delay(10000);
Assert.True(isExecuted);
await service.StopAsync(CancellationToken.None);
}
Here's how I usually do it. You mention you are going to the database to update some data, so I'm assuming you are expecting that as a dependency from BackgroundManager
[Fact]
public void BackgroundManagerUpdatingDataTest()
{
// Arrange
Mock<IDataAccess> dbMock = new Mock<IDataAccess>();
dbMock.Setup(x => x.UpdateSomethingInDB(It.IsAny<BusinessObject>())).Returns(1); // One row updated from the DML in UpdateSomethingInDB from the BusinessObject
BackgroundManager sut = new BackgroundManager(dbMock.Object); // System under test.
// Act
await sut.StartAsync(CancellationToken.None);
await Task.Delay(500); // Give the test some time to execute.
await sut.StopAsync(CancellationToken.None); // Stop the Background Service.
// Assert
dbMock.Verify(x => x.UpdateSomethingInDB(It.IsAny<BusinessObject>()), Times.Exactly(1));
}
Above, we are plainly testing the update to the database occurred by Mocking the data access call and verifying that it was called exactly once.
You could of course Mock any other dependency out using Moq and Assert on anything else you want to verify.

Wrapper around TASKs in C#

I am using tasks in WinForms (.NET 4.0) to perform lengthy operations like WCF call. Application is already in product with heavy use of Tasks (almost all the methods which uses Tasks are void).
During the unit testing we have used AutoResetEvents (in actual code) to find out when the given task is completed then perform assert.
This gives me a thought that almost all the AutoResetEvent are waste of effort. They are just fulfilling unit testing needs, nothing else.
Can we create a wrapper around Tasks likewise when actual code run... they should work in background and in case of unit testing they should be synchronous.
Similar to below link for BackgroundWorker.
http://si-w.co.uk/blog/2009/09/11/unit-testing-code-that-uses-a-backgroundworker/
Why can't you simply use the continuation for tasks in your wrapper, like this:
var task = ...
task.ContinueWith(t => check task results here)
Also, unit tests can be marked as async, if they have a return type Task, so you can use an await there, and after that do your asserts:
[Test]
public async Task SynchronizeTestWithRecurringOperationViaAwait()
{
var sut = new SystemUnderTest();
// Execute code to set up timer with 1 sec delay and interval.
var firstNotification = sut.StartRecurring();
// Wait that operation has finished two times.
var secondNotification = await firstNotification.GetNext();
await secondNotification.GetNext();
// Assert outcome.
Assert.AreEqual("Init Poll Poll", sut.Message);
}
Another approach (from the same article) is to use a custom task scheduler, which will be synchronous in case of unit testing:
[Test]
public void TestCodeSynchronously()
{
var dts = new DeterministicTaskScheduler();
var sut = new SystemUnderTest(dts);
// Execute code to schedule first operation and return immediately.
sut.StartAsynchronousOperation();
// Execute all operations on the current thread.
dts.RunTasksUntilIdle();
// Assert outcome of the two operations.
Assert.AreEqual("Init Work1 Work2", sut.Message);
}
Same MSDN magazine contains nice article about best practices for async unit testing. Also async void should be used only as an event handler, all other methods should have async Task signature.

Searching for nicer implementation for this unit test

I use xUnit and FluentAssertions to write my unit tests and I am stuck at the following problem. As I have not implemented the catch (in GetCountriesAsync) of the WebException yet, I throw a new NotImplementedException in this place.
This code is the only way I made the test actually work as expected. I added the native xUnit implementation either, due to FluentAssertions is just syntactic sugar.
[Fact]
public async Task GetCountriesAsyncThrowsExceptionWithoutInternetConnection()
{
// Arrange
Helpers.Disconnect(); // simulates network disconnect
var provider = new CountryProvider();
try
{
// Act
var countries = await provider.GetCountriesAsync();
}
catch (Exception e)
{
// Assert FluentAssertions
e.Should().BeOfType<NotImplementedException>();
// Assert XUnit
Assert.IsType<NotImplementedException>(e);
}
}
Though I found this implementation a lot nicer, it just doesn't work.
[Fact]
public async Task GetCountriesAsyncThrowsExceptionWithoutInternetConnection3()
{
// Arrange
Helpers.Disconnect(); // simulates network disconnect
var provider = new CountryProvider();
// Act / Assert FluentAssertions
provider.Invoking(async p => await p.GetCountriesAsync())
.ShouldThrow<NotImplementedException>();
// Act / Assert XUnit
Assert.Throws<NotImplementedException>(async () => await provider.GetCountriesAsync());
}
As VS2012/ReSharper already suggests to remove the redundant async keyword of the test method, I replaced async Task with void and the test still behaves the same, so I suspect the async Actions cannot be awaited, they're rather fired and forgotten.
Is there a way to implement this properly with xUnit/FluentAssertions? I think I have to go with my first implementation as I can't see any functionality like InvokingAsync().
Actually, FA 2.0 has specific support for handling asynchronous exceptions. Just look at the unit tests in AsyncFunctionExceptionAssertionSpecs. for various examples.
Regarding FluentAssertions, I've added the following to my code:
using System;
using System.Threading.Tasks;
namespace FluentAssertions
{
public static class FluentInvocationAssertionExtensions
{
public static Func<Task> Awaiting<T>(this T subject, Func<T, Task> action)
{
return () => action(subject);
}
}
}
and now you can do:
_testee.Awaiting(async x => await x.Wait<Foo>(TimeSpan.Zero))
.ShouldThrow<BarException>();
whereas _teste.Wait<T> returns a Task<T>.
Also the naming of the method Awaiting make sense, because pure invocation of the method will not result in the exception being caught by the caller, you do need to use await to do so.