I keep thinking that I am almost done with my latest Dart research chain. And this time, I think that I am, but before I move back to finishing off Gaming JavaScript, I need to figure out how to test futures and isolates in Dart. I have already explored asynchronous testing in Dart quite a bit, but I need to make sure that I have futures working properly.
In Dart for Hipsters, I introduce completers and futures with a simple code example:
main() { var completer = new Completer(); var future = completer.future; future.then((message) { print("Future completed with message: $message"); }); completer.complete("foo"); }Since I immediately complete the completer, the end result of running this code is to simply print out:
Future completed with message: fooThe question is how do I test this. The
print()
method complicates things, but I use my usual trick of overriding print()
for my testing purposes. In this case, I forward the message onto a supplied callback:main() { // ... } var cb; print(msg) => cb(msg);To enable this to be testable in a separate file, I add a
library
declaration at the top of the file:library simple_completer; main() { // ... } var cb; print(msg) => cb(msg);Now, in the test file, I import the unittest library and my
simple_completer.dart
library so that I can run my tests:library completer_test; import 'package:unittest/unittest.dart'; import '../simple_completer.dart' as Simple; run() { group("[simple completers]", (){ test('can complete in the future', (){ // TESTS GO HERE }); }); }But how to test? The Dart matcher libary includes a
completer()
matcher that I would love to use in order to match my expectation. Unfortunately, the completer is self-contained within the library's main()
method. I could manually create a new matcher and complete in the print()
statement, but this seems like cheating. Instead, I fallback to a simple async test: test('can complete in the future', (){
Simple.cb = expectAsync1((msg){
expect(msg, equals("Future completed with message: foo"));
});
Simple.main();
});
I set the callback variable in the Simple libary to a function that accepts a single parameter (that's what the 1 in expectAsync1()
indicates). And inside that function, I set the expectation of the print message.And amazingly, it works:
➜ isolates git:(master) ✗ dart test/test.dart unittest-suite-wait-for-done PASS: [simple completers] can complete in the future All 1 tests passed. unittest-suite-successI am so surprised that this works with my first try that I have to intentionally break it to ensure that I really am testing what I think I am testing. So I change the test to expect a "bar" in the printed message instead of a "foo" and find:
➜ isolates git:(master) ✗ dart test/test.dart unittest-suite-wait-for-done FAIL: [simple completers] can complete in the future Expected: 'Future completed with message: bar' but: was 'Future completed with message: foo'.So it seems that I really do have a test in place. Asynchronous testing in Dart is crazy easy.
Just because I enjoy playing with Dart testing matcher so much, I must see how the completion matcher works. The answer? Pretty much as neatly as do the other testing matchers. I set the expectation on the future as to which message that I expect to receive in the future. Then, when the associated completer finishes at some future point, the test executes:
test('can complete once', (){
var completer = new Completer();
var future = completer.future;
expect(future, completion("foo"));
completer.complete("foo");
});
I am saying that I expect that my future to be a completion with the value "foo", which it is:➜ isolates git:(master) ✗ dart test/test.dart unittest-suite-wait-for-done PASS: [simple completers] can complete in the future PASS: [simple completers] can complete once All 2 tests passed.Very nice. I will pick back up tomorrow with isolates testing, then I really need to get back to Gaming JavaScript.
Day #630
No comments:
Post a Comment