A solid test suite was not an option for the first two editions of Dart for Hipsters. It is now. The advent of a proper unittest package in the Dart Pub means that I have no more excuses for incorrect code. More importantly, I have no excuse for not quickly identifying which code samples are out of date with the latest changes to Dart.
The question still remains, how do I test a code sample that looks something like the following?
var str1 = "foo",
str2 = str1;
str1.hashCode; // 596015325
str2.hashCode; // 596015325
(this demonstrates that string copies point to the original object)I like the format of the code—it fits well in the narrative of the book. I would hate to introduce
print()
or other statements that might obscure the point. But as-is, that is not exactly a method that lends itself to testing. It's not even a method.My first instinct is to put the code for the book inside a multi-line comment and then test similar code. That is less than ideal because I would need to work hard to keep the commented code in sync with the testing code.
In the end, I decide to put this inside a
setUp
block. The variables are local to that function, so I need a way to expose them for subsequent testing—library scoped variables to the rescue:import 'package:unittest/unittest.dart'; String _str1, _str2; main() { setUp((){ /* ------------------------- */ var str1 = "foo", str2 = str1; str1.hashCode; // 596015325 str2.hashCode; // 596015325 /* ------------------------- */ _str1 = str1; _str2 = str2; }); test('"foo" is 596015325', (){ expect(_str1.hashCode, equals(596015325)); }); test('copy of "foo" is 596015325', (){ expect(_str2.hashCode, equals(596015325)); }); }Using private variables to mirror the variables in the code sample is a choice of convenience rather than a need for them to be private. I get to use the same name, just with a leading underscore. This should make it obvious what is going on when I revisit this test a few months down the line.
The actual value of the hash code is not too important, so I may change it to a
setUp
variable in the future if I find that this causes trouble. In the end, it is easy to visually see where the actual code snippet is. It is easy for the book software to grab that same extract. And I have a test that passes:
$ dart strings.dart unittest-suite-wait-for-done PASS: "foo" is 596015325 PASS: copy of "foo" is 596015325 All 2 tests passed. unittest-suite-successThat is one snippet. How do I run multiple snippets at the same time? I cannot simply pass multiple dart tests to the dart interpreter because they would all have a
main()
entry point:# Only the string concatenation tests run: $ dart strings.dart string_concat.dart unittest-suite-wait-for-done PASS: "foo" is 596015325 PASS: copy of "foo" is 596015325 All 2 tests passed. unittest-suite-successI could put all of the tests into one big file. For various reasons, it would be better to keep the code snippets in separate files. In Dart, that means either
import
of libraries or including the separate files as a part
of the whole.I opt for
import
because that will allow me to use the same method name in each executable snippet. That is, instead of main()
in my tests, I use run()
:library strings_snippet; import 'package:unittest/unittest.dart'; String _str1, _str2; run() { group("[strings]", (){ setUp((){ /* ------------------------- */ // code snippet here... /* ------------------------- */ // ... }); // tests here... }); }Aside from the new
run()
method name, I also add a group
call around my test to better distinguish the tests when all of them are run together. And, of course, I need a library
statement at the top so that I can import the library into the main test.dart
file.As for the main test file, it is relatively simple:
import 'strings.dart' as Strings; import 'string_concat.dart' as StringConcat; main () { Strings.run(); StringConcat.run(); }Even though both libraries define a
run()
method, I can still import and use them thanks to Dart's library namespacing. The end result is quite readable—I rather like it.And better still, I have some very nice test output verifying that my code snippets work with the most recent Dart:
$ dart test.dart unittest-suite-wait-for-done PASS: [strings] "foo" is 596015325 PASS: [strings] copy of "foo" is 596015325 PASS: [string concat] "foo" is 596015325 PASS: [string concat] "foo".concat("bar") is 961740263 All 4 tests passed. unittest-suite-successWell, at least the version of Dart from three days ago. Who knows what could be broken now? Actually I can—thanks to my nifty test suite.
Day #606
No comments:
Post a Comment