Wednesday, February 22, 2012

Testing Library Code in Dart

‹prev | My Chain | next›

Having gotten a dummy Dart unit test to pass last night, I try my hand at testing some of the Hipster MVC library code with which I have been playing.

First, I copy the testing directory from dart bleeding edge into my app. Tonight I store the contents of testing, the unittest and darttest sub-directories, under tests/lib in my main application directory:
➜  dart-comics git:(mvc-sync) ✗ tree -d tests 
lib
+-- tests
    +-- dartest
    |   +-- resources
    +-- unittest

4 directories
In tests, I recreate last night's index.html test runner:
<html>
<head>
  <script type="application/dart" src="HipsterCollectionTest.dart"></script>

  <script type="text/javascript">
    // start dart
    navigator.webkitStartDart();
  </script>
</head>

<body>
<h1>Test!</h1>

</body>
</html>
The HipsterCollectionTest.dart test file will, of course, test my collection base class. I start with a test skeleton to ensure that everything in the testing library is in the proper place:
#import('lib/unittest/unittest_dartest.dart');
#import('lib/dartest/dartest.dart');

main() {
  test('Test Description',(){
    Expect.equals(3, myAddFunc(1,2));
  });
  new DARTest().run();
}

int myAddFunc(int x, int y) => x + y;
And, indeed, when I open tests/index.html and run the tests, I see green:


Next, I #import() the HipsterCollection library and test the simplest thing that I can think of—that the collection holds multiple models:
#import('../public/scripts/HipsterCollection.dart');

main() {
  test('HipsterCollection has multiple models', (){
    HipsterCollection it = new HipsterCollection();
    it.models = [{'id': 17}, {'id': 42}];

    Expect.equals(2, it.length);
  });
  new DARTest().run();
}
Trying it out in the browser, it works:


To be sure I have not made a useless test, I intentionally break it:
    // ...
    Expect.equals(0, it.length);
    // ...
This results in red:


So I have succeeded in testing actual Dart code. Yay!

After adding a few more tests, I pine for two things: a better way to test Map equality and a way to group tests:
  test('HipsterCollection can lookup a model by ID', () {
    HipsterCollection it = new HipsterCollection();
    it.models = [{'id': 17}, {'id': 42}];

    Expect.listEquals([17], it[17].getValues());
    Expect.listEquals(['id'], it[17].getKeys());
  });

  test('HipsterCollection lookup is null when it does hold ID', () {
    HipsterCollection it = new HipsterCollection();
    it.models = [{'id': 17}, {'id': 42}];

    Expect.isNull(it[1]);
  });
Fortunately, bleeding edge test has a solution for the latter problem—I can group() tests:
  group('HipsterCollection lookup', () {
    HipsterCollection it = new HipsterCollection();
    it.models = [{'id': 17}, {'id': 42}];

    test('works by ID', () {
      Expect.listEquals([17], it[17].getValues());
      Expect.listEquals(['id'], it[17].getKeys());
    });

    test('is null when it does not hold ID', () {
      Expect.isNull(it[1]);
    });
  });
Nice! I can reuse the same test subject, it for both lookup tests. I am not a fan of indirection in tests, but I do not mind simple re-use where possible.

I am in something of a bind with comparing Maps because the following fails:
  test('map equality', () {
    Expect.equals({'foo': 'bar'}, {'foo': 'bar'});
  });
I settle for declaring model variables and testing that the results of collection lookup is the same as the original model:
  group('HipsterCollection lookup', () {
    var model1 = {'id': 17},
        model2 = {'id': 42};

    HipsterCollection it = new HipsterCollection();
    it.models = [model1, model2];

    test('works by ID', () {
      Expect.listEquals([17], it[17].getValues());
      Expect.listEquals(['id'], it[17].getKeys());
      Expect.equals(model1, it[17]);
    });
    // ...
  });
Even with that passing, I opt to leave the two listEquals for the keys and values of the Map—the output on failure is easier to read.

This will do as a good stopping point for tonight. The remainder of HipsterCollection is Ajax or Event related. That is going to require a different kind of testing. Tomorrow.


Day #304

No comments:

Post a Comment