Monday, August 19, 2013

Dart Love: Tests, Packages, and Cascades


I am drunk on the power of Dart Pub.

I have been imbibing heavily as I have driven the development of the ctrl_alt_foo library. So heavily, in fact, that I can't quite remember how I got here. The last thing I remember, I was fiddling around with a library that supplied a test server and now I find that I have been working on a client-side library for keyboard events over the past week. It's so odd…

But I really need that test server library, so I am going to ease my way back into it by incorporating it into the test suite for the code from Dart for Hipsters. Thanks to the amazing power of Dart's pub packaging, that should be quite easy. Dart is just that powerful!

Must. Remain. Sober. In the face. Of overwhelming. Awesome.

Normally I will leave myself a failing test (they are all passing currently) or TODO notes (there are some but they seem done). So the best course of action really does seem to be to try it out in live code. If all goes well, I can publish the library. If not, I will have something to work on tomorrow. This also allows me to get back to the writing of the next edition of the book. Best of all, I can continue to drink of the sweet, sweet nectar of the Pub...

In the pubspec.yaml file for the Dart for Hipsters code, I include a path dependency for plummbur_kruk (the name of my fake HTTP test server):
name: DartForHipsters
dependencies:
  unittest: any
  browser: any
  plummbur_kruk:
    path: /home/chris/repos/plummbur-kruk
With that, any changes that I make in the local copy of plummbur_gruk are immediately available to the Dart for Hipsters tests. It is a this point that I realize that I have already done some of this as well. I really need to lay off this stuff…

I am already pulling plummbur_kruk into my dummy_test.dart:
import 'package:plummbur_kruk/server.dart' as Server;

main()=> Server.main();
This allows me to start the server in my test_runner.sh Bash script:
# Start the test server
  dart dummy_server.dart &
  server_pid=$!

  # Run a set of Dart Unit tests
  # ...

  # Stop the server
  kill $server_pid
  rm -f test.db test/test.db
OK. I may have done that, but I do not have a test that verifies that the plummbur_kruk server is running. If it is down, it might take me precious seconds to realize what has gone wrong—SECONDS I say! So I write a test assuring myself that I will never want for those seconds while working with the Dart for Hipsters code:
import 'package:unittest/unittest.dart';
import 'dart:html';
import 'dart:async';
import 'package:plummbur_kruk/kruk.dart';

main () {
  test("that the mighty Kruk is alive", (){
    expect(Kruk.isAlive, isTrue);
  });
  // actual book tests...
}
And that fails! I am pretty excited because I have finally reached the boundaries of where I was working before I found myself in the pub. This test is failing not because the plummbur_kruk test server is down, but because the plummbur_kruk code is not checking for it properly.

The problem is that my tests are being loaded from the file system (in a file:// web page context), but Kruk.isAlive is trying to access a root relative URL:
class Kruk {
  static String SERVER_ROOT = 'http://localhost:31337';
  // ...
  static bool get isAlive {
    var req = new HttpRequest();
    req.open('get', '/ping', async: false);
    return req.status == 200;
  }
}
Yikes! Now that I think about it, I am not even sending that request so there is no way that it will pass. I rewrite the isAlive predicate method as:
class Kruk {
  static String SERVER_ROOT = 'http://localhost:31337';
  // ...
  static bool get isAlive {
    var req = new HttpRequest()
      ..open('get', '${SERVER_ROOT}/ping', async: false)
      ..send();

    return req.status == 200;
  }
}
That still does not pass, but only because I need the server to response to the /ping resource (I already have CORS headers set):
      if (path == '/ping') {
        return req.response
          ..statusCode = HttpStatus.OK
          ..close();
      }
With that, I have:
PASS: the mighty Kruk is alive
I feel a bit like Socrates in Bill and Ted's Excellent Adventure here, because I cannot decide what I love most about San Dimas… er, Dart. Because, wow, do I love slinging them method cascades (the double .. in both code examples above), which invoke a method, but return the receiver, not the return value of the method. So nice for organizing code.

The last thing that I am going to do is be a good tester. I found that plummbur_kruk bug when trying to use it in the Dart for Hipsters application. I need to write a test in plummbur_kruk itself to ensure that I do not introduce regressions at some point down the line. Thankfully, it is not much work to Do the Right Thing™. I already have a Kruk group of tests back in plummbur_kruk. I add my is-alive test to the group:
  group("The Mighty Kruk", (){
    test("is alive", (){
      expect(Kruk.isAlive, isTrue);
    });
    // ...
  });
That seems a good stopping point for today. Time to go sleep it off. If that's even possible!



Day #848

No comments:

Post a Comment