Monday, October 7, 2013

Staircase to Dependency Hell

One of the many reasons that I love Dart is the testing. The built-in unittest is pretty darn amazing. And people are already building things like scheduled_test on top of it. I still haven't figured out when to use unittest and when to use scheduled test, but after last night, I may have found at least one rule of thumb.

What bothers me about this is the crazy staircase of curly braces at the end. Admittedly, this is partly due to my stubbornness in insisting on one-concept-per-line-of-code. Still, staircases only appear in code when nested dependencies are present and this is no exception.

So let's see if scheduled_test can help. The scheduled_test add-on to unittest breaks asynchronous tasks into “schedules.” Hopefully that will be of particular use with the Timer.run(). I start by replacing unittest in the dev_dependencies section of my package's pubspec.yaml file:

And, since scheduled_test is a drop-in replacement for unittest, all of my tests continue to pass:

PASS: [polymer] can embed code
PASS: [polymer] can set line number
PASS: [polymer] creates a shadow preview
PASS: [polymer] creates an editor
PASS: [polymer] multiple elements can embed code
PASS: [polymer] multiple elements after JS has loaded and been evaluated can still embed code
All 6 tests passed.

Although the Timer.run() section of my test is bothering more than other parts of the test, I will start at the the top. The “after JS has loaded and been evaluated” group was written only to get the setUp() to block until the returned Editor.jsReady future completed. With scheduled_test, I no longer need that setUp() allowing me to move the Future into a schedule of the actual test:

The next schedule in my test is adding my custom Polymer element to the DOM and then waiting one event loop for the ice-code-editor element to update the DOM. In the unittest version, the Timer.run() served the purpose of waiting one event loop. In the scheduled_test version, I can put that wait back where it belongs—with the code that adds the element to the DOM.

I cannot use a Timer.run() this time. Instead, I need to return a Future that will wait one event loop before completing. The easiest way that I know to do that is with the named Future.delayed constructor. A delay of zero should wait one event loop:

More importantly, I have my staircase down to three steps. Given that the concepts in those steps are: test expectation, schedule, and test, I think I can live with that. Maintainability in tests is not quite as important as it is in actual code, but little wins like this can go a long way.