Skip to main content

Unboxing Packages: A Requiem for scheduled_test

Long long ago in the dawning days of Dart, when Futures were new and Streams unknown, Bob saw the need for a package manager to bring the people together. Together he and I spun formless bits into code and wove that code into pub, which even today carries code into the eager hands of Dartisans. In those olden days, we had no modern marvels such as the twins async and await, and our tests grew heavy and graceless under the weight of their chain()s1.

So it was that I came up with an idea for a new way of writing tests. Instead of gluing the steps together with the cumbersome Future API, each step would register an asynchronous callback in a queue. Once all the steps were queued up, the library would run them in order. All the API boilerplate would be hidden away from the user, and the tests would look nice and clean. After honing this technique on pub’s tests, I brought it out into its own package, which I called scheduled_test.

If this whole idea of queuing up tasks and running them later sounds complicated, that’s because it was. scheduled_test did a great job of pushing complexity away from test cases, but in return any infrastructure that ran those test cases got much more complex. You couldn’t just write normal Dart code, you had to make sure to wrap everything in schedule() and allow users to pass in Futures and all sorts of hassle. I had created a better sort of tests, but not one without flaws.

The Beginning of the End

Then in Dart 1.9, support for async and await landed and completely changed the equation. No longer did asynchronous APIs have to involve a mess of nesting and callbacks—even the most Future-heavy tests were free to look like plain old synchronous code with some extra keywords sprinkled on top. The driving purpose of scheduled_test, making asynchronous tests look simple and comprehensible, was now supported by the language itself.

You may then wonder, why didn’t scheduled_test fade into obscurity and disuse fifteen Dart versions ago? You may ask, “Natalie, when you created the new test package, why did you decide to use scheduled_test when it no longer had a purpose?” The answer to these questions is infrastructure.

Over the years we had built up a number of APIs around the core functionality of scheduled_test, and even when that functionality got less relevant the APIs that used it were very useful. We couldn’t migrate old code until we had a good replacement, and even for new code scheduled_test was still the best option for testing files and subprocesses.

The Final Blow

This year2, one of my goals was to finally rid the world of scheduled_test once and for all. This meant buckling down and replacing all the infrastructure we’d built on it over the years with new packages that use nothing more than dart:async and the base test API. It took a lot of work, but I did it. I’m pleased to say that pub no longer uses scheduled_test, nor does test. These were scheduled_test’s biggest users, and they serve as a proof-of-concept for the suitability of its replacements:

  • The scheduled_test/descriptor library, which provided a simple and readable API for creating and validating files and directories, is replaced by the test_descriptor package.

  • The scheduled_test/scheduled_process library, which wrapped dart:io’s Process library to provide better debugging and easier line-by-line access to the standard IO streams, is replaced by the test_process package.

  • The scheduled_test/scheduled_stream library provided a pull-based API for streams which has been replaced by the async package’s StreamQueue class, and the stream matchers have been replaced by a suite of matchers in test itself.

  • The scheduled_test/scheduled_server library, which provided a server that validates and handles individual requests, has been moved to the shelf_test_handler package.

  • The core of scheduled_test may be going away, but it had a few useful features that I salvaged. The test package can now print a message only when a test fails and add tear-downs at runtime, both of which were originally supported in scheduled_test only.

I hope these APIs can be used more widely now that they’re no longer tangled up with scheduled_test. In fact, I encourage you to try them out right away if they seem useful—or write your own libraries if they don’t!


  1. Fun fact: Future.then() used to be called Future.chain()!
  2. Excluding April and parts of March and May, when I was out recovering from surgery. I’m doing great, thanks for asking! 😊

Popular posts from this blog

A stronger Dart for everyone

We are constantly asking ourselves:
How do we make developers even more productive when writing Dart apps? We believe that a critical part of the answer to this question is to make strongmode – a sound static type system for Dart – the standard for all Dart developers.

Teams that use Dart to build apps like Soundtrap, AdWords, AdSense, and Greentea say they really enjoy using strong mode features, such as early error detection. In fact, teams that have switched completely to strong mode cite not only early error detection but also better code readability and maintainability as major benefits. We hear this both from small teams and – even more so – from large teams with hundreds of developers writing and maintaining millions of lines of Dart code. As Björn Sperber from Soundtrap says,
Strong mode and the smooth integration with IntelliJ is a joy to use and a huge improvement. If you’ve tried out Flutter, you’ve already used strong mode checks from the Dart analyzer.

Given the benefits …

AngularDart 4

AngularDart v4 is now available. We've been busy since the release angular2 v3.1.0 in May. Not only did we "drop the 2", but we also improved the compiler and tightened up the framework to give you smaller code, we updated the package structure to improve usability, and we added several new features. Check out the updated documentation to get started.
Just angular Upgrading to v4 will require more than updating your version constraint. The package has changed names (back) to angular – dropping the 2. You'll need to update your pubspec.yaml and the corresponding imports in your code. In most instances, find-and-replace should do the trick. Going forward, the package will be called package:angular. We'll just update the version number.
Smaller code The updated compiler in 4.0 allows type-based optimizations that not only improve runtime performance but generate better code because we are able to strongly type templates. A big result of the update is that many ap…

Dart 1.24: Faster edit-refresh cycle on the web & new function type syntax

Dart 1.24 is now available. It includes the Dart Development Compiler and supports a new generic function type syntax. Get it now!


Figure 1: DDC debugging in Chrome.

Some notable changes in this release:
pub serve now has support for the Dart Development Compiler. Unlike dart2js, this new compiler is modular, which allows pub to do incremental re-builds for pub serve.In practice what that means is you can edit your Dart files, refresh in Chrome (or other supported browsers), and see your edits almost immediately. This is because pub is only recompiling your package, not all packages that you depend on.There is one caveat with the new compiler, which is that your package and your dependencies must all be strong mode clean.You can also use the new compiler to run your tests in Chrome much more quickly than you can with dart2js.Read more in the changelog.You can now publish packages that depend on the Flutter SDK to pub. Moreover, pub.dartlang.org has started tagging Flutter plugins with …