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.
Related
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();
}
I am new to kotlin multiplatform library.
I wanted to make a simple HTTP get request and test if it works.
here is what I have so far.
this is in the commonMain package
import io.ktor.client.*
import io.ktor.client.request.*
object HttpCall {
private val client: HttpClient = HttpClient()
suspend fun request(url: String): String = client.get(url)
}
and here is my attempt to test
#Test
fun should_make_http_call() {
GlobalScope.launch {
val response = HttpCall.request("https://stackoverflow.com/")
println("Response: ->$response")
assertTrue { response.contains("Stack Overflow - Where Developers Learn") }
assertTrue { response.contains("text that does not exist on stackoverflow") }
}
Now, this should fail because of the second assert but it doesn't.
no matter what I do the test always passes.
and printing the response does not work either
what am I doing wrong here?
The test function will run in a single thread, and if the function ends without failing, the test passes. GlobalScope.launch starts an operation in a different thread. The main test thread will finish before the network calls get a chance to run.
You should be calling this with something like runBlocking, but testing coroutines in general, and ktor specifically, on Kotlin native, is not easy because there's no easy way to have the suspended function continue on your current thread.
I will not use the GlobalScope or the runBlocking because they are not really made for Unit Test. Instead, I will use runTest.
Here are the steps:
Check your build.gradle and make sure you do have under commontTest the lib 'kotlinx-coroutines-test' set
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Version.kotlinCoroutines}")
...
}
}
Then into your directory commonTest, create a file and run
#Test
fun should_make_http_call() = runTest {
val response = HttpCall.request("https://stackoverflow.com/")
println("Response: ->$response")
assertTrue { response.contains("Stack Overflow - Where Developers Learn") }
assertTrue { response.contains("text that does not exist on stackoverflow") }
}
Extra:
runTest does not handle exceptions very well, so if you are interested in making to catch any exceptions if happens. Change runTest for runReliableTest, You can get the code from this link https://github.com/Kotlin/kotlinx.coroutines/issues/1205#issuecomment-1238261240
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.
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.
I'm writing many unit tests in VS 2010 with Microsoft Test. In each test class I have many test methods similar to below:
[TestMethod]
public void This_is_a_Test()
{
try
{
// do some test here
// assert
}
catch (Exception ex)
{
// test failed, log error message in my log file and make the test fail
}
finally
{
// do some cleanup with different parameters
}
}
When each test method looks like this I fell it's kind of ugly. But so far I haven't found a good solution to make my test code more clean, especially the cleanup code in the finally block. Could someone here give me some advices on this?
Thanks in advance.
If you really want to handle and log exceptions whilst test execution you can wrap up this standard template in a helper method and use like shown below [*].
But if exceptions is a part of test case this is wrong approach and you should use facilities provided by a test framework, for instance NUnit provides such helpers to test exceptions:
Assert.Throws<ExceptionType>(() => { ... code block... });
Assert.DoesNotThrow(() => { ... code block... });
And to do cleanup special method attributes like [TestCleanup] and [TestInitialize] to do test initialization and cleanup automatically by a test framework.
[*] The idea is to wrap test body in a delegate and pass into the helper which actually perform test execution wrapped in the try/catch block:
// helper
public void ExecuteTest(Action test)
{
try
{
test.Invoke();
}
catch (Exception ex)
{
// test failed, log error message in my log file and make the test fail
}
finally
{
// do some cleanup with different parameters
}
}
[TestMethod]
public void This_is_a_Test_1()
{
Action test = () =>
{
// test case logic
// asserts
};
this.ExecuteTest(test);
}
Clear all try-catch-finally (especially catch is not only unnecessary, but even harmful, you should not catch exception when testing) and do the cleanup in tearDown method (however is it done in MS Test, I would presume there will be something like [TearDownMethod] or [FixtureTearDown] or something like that).
Have you considered using the ExpectedException attribute on the test method?
http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.expectedexceptionattribute.aspx