How to use `expectAsync2` correctly when writing dart unittest? - unit-testing

I was trying this method expectAsync2, so there was this question: Why the async test passed, but there are some error messages displayed?
But it seems I didn't use it correctly. Is there any good example of expectAsync2?

In the referenced question expectAsync was just used to guard a async call so that the test doesn't end before the call of new Timer(...) finishes.
You can additionally add provide how often (min/max) the method has to be called to satisfy the test.
If your tested functionality calls a method with more than one parameter you use `expectAsync2)
The mistake in your referenced question was, that your call to expectAsyncX was delayed too.
The call to expectAsyncX has to be made before the async functionality is invoked to register which method has to be called.
library x;
import 'dart:async';
import 'package:unittest/unittest.dart';
class SubjectUnderTest {
int i = 0;
doSomething(x, y) {
i++;
print('$x, $y');
}
}
void main(List<String> args) {
test('async test, check a function with 2 parameters', () {
var sut = new SubjectUnderTest();
var fun = expectAsync2(sut.doSomething, count: 2, max: 2, id: 'check doSomething');
new Timer(new Duration(milliseconds:200), () {
fun(1,2);
expect(sut.i, greaterThan(0));
});
new Timer(new Duration(milliseconds:100), () {
fun(3,4);
expect(sut.i, greaterThan(0));
});
});
}
You can check what happens if you set count and max to 3.

You can have a look at the Asynchronous tests section of the article Unit Testing with Dart.

Related

How to not call a function inside another function in Unit test Dart?

I want to not call a few function calls that are inside a function that I am testing. I am new to testing. Since I am using SetUpALL((){}); to create a test suitable case instead of using the function.
main file:
class A{
Future<void> function_1 () async {
await another_function_1 // want to igonre this function in test.
}
}
test file:
void main {
setUpAll((){
// statements.
});
group('tests',(
test('test 1',(){
A().function1(); // calling the function here for testing.
});
){
});
}
Welcome to the world of "Designing Testable Classes". You'll probably need to refactor this method to extract a testable part of the operations. Also, learn about Mockito to stub out dependent classes with your own behavior.

How to unit test function that has coroutine `GlobalScope.launch`

I have this function
override fun trackEvent(trackingData: TrackingData) {
trackingData.eventsList()
}
And I could have my test as below.
#Test
fun `My Test`() {
// When
myObject.trackEvent(myTrackingMock)
// Then
verify(myTrackingMock, times(1)).eventsList()
}
However, if I make it into a
override fun trackEvent(trackingData: TrackingData) {
GlobalScope.launch{
trackingData.eventsList()
}
}
How could I still get my test running? (i.e. can make the launch Synchronous?)
I created my own CoroutineScope and pass in (e.g. CoroutineScope(Dispatchers.IO) as a variable myScope)
Then have my function
override fun trackEvent(trackingData: TrackingData) {
myScope.launch{
trackingData.eventsList()
}
}
Then in my test I mock the scope by create a blockCoroutineScope as below.
class BlockCoroutineDispatcher : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
block.run()
}
}
private val blockCoroutineScope = CoroutineScope(BlockCoroutineDispatcher())
For my test, I'll pass the blockCoroutineScope in instead as myScope. Then the test is executed with launch as a blocking operation.
To approach the answer, try asking a related question: "How would I unit-test a function that has
Thread { trackingData.eventsList() }
in it?"
Your only hope is running a loop that repeatedly checks the expected condition, for some period time, until giving up and declaring the test failed.
When you wrote GlobalScope.launch, you waived your interest in Kotlin's structured concurrency, so you'll have to resort to unstructured and non-deterministic approaches of testing.
Probably the best recourse is to rewrite your code to use a scope under your control.
I refactored my method to
suspend fun deleteThing(serial: String): String? = coroutineScope {
This way, I can launch coroutines with launch
val jobs = mutableListOf<Job>()
var certDeleteError: String? = null
certs.forEach { certArn ->
val job = launch {
deleteCert(certArn, serial)?.let { error ->
jobs.forEach { it.cancel() }
certDeleteError = error
}
}
jobs.add(job)
}
jobs.joinAll()
For the test, I can then just use runTest and it runs all of the coroutines synchronously
#Test
fun successfullyDeletes2Certs() = runTest {
aws.deleteThing("s1")
Now you just need to mind your context where you are calling the deleteThing function. For me, it was a ktor request, so I could just call launch there also.
delete("vehicles/{vehicle-serial}/") {
launch {
aws.deleteThing(serial)
}
}

How to unit test Kotlin suspending functions

I follow the MVP pattern + UseCases to interact with a Model layer. This is a method in a Presenter I want to test:
fun loadPreviews() {
launch(UI) {
val items = previewsUseCase.getPreviews() // a suspending function
println("[method] UseCase items: $items")
println("[method] View call")
view.showPreviews(items)
}
}
My simple BDD test:
fun <T> givenSuspended(block: suspend () -> T) = BDDMockito.given(runBlocking { block() })
infix fun <T> BDDMockito.BDDMyOngoingStubbing<T>.willReturn(block: () -> T) = willReturn(block())
#Test
fun `load previews`() {
// UseCase and View are mocked in a `setUp` method
val items = listOf<PreviewItem>()
givenSuspended { previewsUseCase.getPreviews() } willReturn { items }
println("[test] before Presenter call")
runBlocking { presenter.loadPreviews() }
println("[test] after Presenter call")
println("[test] verify the View")
verify(view).showPreviews(items)
}
The test passes successfully but there's something weird in the log. I expect it to be:
"[test] before Presenter call"
"[method] UseCase items: []"
"[method] View call"
"[test] after Presenter call"
"[test] verify the View"
But it turns out to be:
[test] before Presenter call
[test] after Presenter call
[test] verify the View
[method] UseCase items: []
[method] View call
What's the reason of this behaviour and how should I fix it?
I've found out that it's because of a CoroutineDispatcher. I used to mock UI context with EmptyCoroutineContext. Switching to Unconfined has solved the problem
Update 02.04.20
The name of the question suggests that there'll be an exhaustive explanation how to unit test a suspending function. So let me explain a bit more.
The main problem with testing a suspending function is threading. Let's say we want to test this simple function that updates a property's value in a different thread:
class ItemUpdater(val item: Item) {
fun updateItemValue() {
launch(Dispatchers.Default) { item.value = 42 }
}
}
We need to somehow replace Dispatchers.Default with an another dispatcher only for testing purposes. There're two ways how we can do that. Each has its pros and cons, and which one to choose depends on your project & style of coding:
1. Inject a Dispatcher.
class ItemUpdater(
val item: Item,
val dispatcher: CoroutineDispatcher // can be a wrapper that provides multiple dispatchers but let's keep it simple
) {
fun updateItemValue() {
launch(dispatcher) { item.value = 42 }
}
}
// later in a test class
#Test
fun `item value is updated`() = runBlocking {
val item = Item()
val testDispatcher = Dispatchers.Unconfined // can be a TestCoroutineDispatcher but we still keep it simple
val updater = ItemUpdater(item, testDispatcher)
updater.updateItemValue()
assertEquals(42, item.value)
}
2. Substitute a Dispatcher.
class ItemUpdater(val item: Item) {
fun updateItemValue() {
launch(DispatchersProvider.Default) { item.value = 42 } // DispatchersProvider is our own global wrapper
}
}
// later in a test class
// -----------------------------------------------------------------------------------
// --- This block can be extracted into a JUnit Rule and replaced by a single line ---
// -----------------------------------------------------------------------------------
#Before
fun setUp() {
DispatchersProvider.Default = Dispatchers.Unconfined
}
#After
fun cleanUp() {
DispatchersProvider.Default = Dispatchers.Default
}
// -----------------------------------------------------------------------------------
#Test
fun `item value is updated`() = runBlocking {
val item = Item()
val updater = ItemUpdater(item)
updater.updateItemValue()
assertEquals(42, item.value)
}
Both of them are doing the same thing - they replace the original Dispatchers.Default in test classes. The only difference is how they do that. It's really really up to you which of them to choose so don't get biased by my own thoughts below.
IMHO: The first approach is a little too much cumbersome. Injecting dispatchers everywhere will result into polluting most of the classes' constructors with an extra DispatchersWrapper only for a testing purpose. However Google recommends this way at least for now. The second style keeps things simple and it doesn't complicate the production classes. It's like an RxJava's way of testing where you have to substitute schedulers via RxJavaPlugins. By the way, kotlinx-coroutines-test will bring the exact same functionality someday in future.
I see you found out on you own, but I'd like to explain a bit more for the people that might run into the same problem
When you do launch(UI) {}, a new coroutine is created and dispatched to the "UI" Dispatcher, that means that your coroutine now runs on a different thread.
Your runBlocking{} call create a new coroutine, but runBlocking{} will wait for this coroutine to end before continuing, your loadPreviews() function creates a coroutine, start it and then return immediately, so runBlocking() just wait for it and return.
So while runBlocking{} has returned, the coroutine that you created with launch(UI){} is still running in a different thread, that's why the order of your log is messed up
The Unconfined context is a special CoroutineContext that simply create a dispatcher that execute the coroutine right there on the current thread, so now when you execute runBlocking{}, it has to wait for the coroutine created by launch{} to end because it is running on the same thread thus blocking that thread.
I hope my explanation was clear, have a good day

Injection in the setUp() method causes the framework not to wait for the Future to complete

I am testing an AngularDart component. I am trying to fetch the template and put it in TemplateCache in the setUp() method. For this I need to inject the template cache. However the inject in the setUp() makes the framework continue to the test method and not waiting for the Future to complete. Here is a simplified example.
import 'dart:async';
import 'package:angular/angular.dart';
import 'package:mock/mock.dart';
import 'package:unittest/unittest.dart';
import 'package:angular/mock/test_injection.dart';
import 'package:angular/mock/module.dart';
import 'package:di/di.dart';
class MyTest {
static main() {
group("SetUp with future that waits", () {
setUp(() {
return new Future.value("First").then((_) => print(_));
});
test("Welcome to the world of tomorrow!", () {
print("Second");
});
});
group("SetUp with future that doesn't wait", () {
setUp(inject((Injector inject) { // injection causes the test to not wait
return new Future.value("First").then((_) => print(_));
}));
test("Welcome to the world of tomorrow!", () {
print("Second");
});
});
}
}
In the console you can see the printed messages: First, Second, Second, First.
I think it must be that the inject is not returning the Future. What else can I do to both have the framework injecting objects that I need and waiting for the Future in the setUp()?
This is what I needed. The mistake was trying to return something from the inject itself. It is actually as simple as this:
setUp(() {
// ...
inject((Injectable injectable) {
// inject the objects here and save them in variables
});
// work with the variables
return new Future.value("First").then((_) => print(_));
});

Qooxdoo: How to call MMock.verify() in an asynchronous test

I'm trying to use qx.dev.unit.MMock.verify() in an asynchronous situation effectively the same as this:
, test_no_output_from_verify: function () {
var mocker = this.mock({ callMeOnce: function () { } });
mocker.expects('callMeOnce').once();
qx.event.Timer.once(function () {
mocker.verify();
this.resume();
}, this, 100);
this.wait(1000);
}
I find that, when the verify() call throws an exception (because the callMeOnce function was not called), that exception is swallowed by the Test Runner. It carries on waiting for the 1000ms timeout to elapse, and then throws the standard timeout exception: 'Asynchronous Test Error: Timeout reached before resume() was called.'.
Is there any way that I can make Test Runner halt immediately and display the exception thrown from mocker.verify() instead?
==============================
EDIT: Reading the manual one more time after posting the question shows my mistake. The correct technique is to put the verify() call inside the resume(), thus:
, test_no_output_from_verify: function () {
var mocker = this.mock({ callMeOnce: function () { } });
mocker.expects('callMeOnce').once();
qx.event.Timer.once(function () {
this.resume(function () {
mocker.verify();
});
}, this, 100);
this.wait(1000);
}
This works as expected and I get the ExpectationError: Expected callMeOnce([...]) once (never called) message inside the Test Runner.