Saturday, May 31, 2014

The Unfortunate Case of the Annotation and Polymer Testing


OK. Today I test model driven attributes in a Dart Polymer element.

I was able to update the attributes in the pizza-building <x-pizza> element yesterday:



I can now set toppings and query them on the various toppings attributes that I added to the Polymer element. And it was quite easy. What was surprisingly difficult was testing the darn thing.

Actually, it was not testing the Polymer element that was the problem. It was testing in general. I have not one, but two approaches to Dart testing in Patterns in Polymer. One is more of a straight unit testing solution largely based on the current stable 0.9 Polymer.dart release. The other is a page objects solution that I based on the unstable 0.10 release. Neither seems to be working for me—at least not without some changes.

It sounds as though the initialization in the unstable 0.10 release, which had changed fairly significantly, is again changing back to something closer to the 0.9 initialization process. So I am going to stick with the stable Polymer.dart (currently 0.9.5+2) in my pubspec.yaml:
name: svg_example
dependencies:
  polymer: any
  polymer_elements: any
dev_dependencies:
  scheduled_test: any
transformers:
- polymer:
    entry_points: web/index.html
To get unstable, I would need to change the “any” dependency constraint to something like: ">=0.10.0-pre".

The HTML page that serves as the testing context is fairly simple in 0.9—it need only load my Polymer component definition and the tests:
<head>
  <!-- Load component(s) -->
  <link rel="import" href="packages/svg_example/elements/x-pizza.html">

  <!-- The actual tests -->
  <script type="application/dart" src="test.dart"></script>
</head>
My test.dart file does the usual initPolymer() inside of the main() entry point to kick things off:
library x_pizza_test;

import 'dart:html';
import 'dart:async';
import 'dart:convert';
import 'package:polymer/polymer.dart';
import 'package:scheduled_test/scheduled_test.dart';

@initMethod
main() {
  initPolymer();

  // Actual tests go here...
}
But when I try to run that test by loading the page (either in Dartium or content_shell), I get a big old stacktrace from deep within Polymer that mostly amounts to:
Uncaught Error: NotSupportedError: Registration failed for type 'polymer-element'. A type with that name is already registered.
This, and a few other related errors had me stuck. It turns out that the @initMethod annotation above the main() entry point in test.dart was the culprit. I had pulled that back from the 0.10 code and figured that it was just an annotation—and one that probably had no effect in 0.9 code—so how much harm could it cause? Well the answer is that it seems to pull in enough Polymer code to cause my stack trace.

After removing the @initMethod annotation, my tests… still fail. They fail, but don't error out. The failures are due to differences between the MDV approach that I have in this “play” Polymer example and the one that I used in the actual book code. The actual book code is cleaner, but I am not going to worry about clean code in my play area. Instead, I update the XPizzaComponent page object class to extract topping information from the attributes that I want to test anyway (I have an internal property in the “real” code):
class XPizzaComponent {
  PolymerElement el;
  XPizzaComponent(this.el);

  // Extract information from the page
  String get wholeToppings => el.attributes['toppings'];
  String get firstHalfToppings => el.attributes['toppings1'];
  String get secondHalfToppings => el.attributes['toppings2'];
  // ...
}
Then I can write my nice, clean Page Objects tests that verify that my new attributes behave as desired:
  group("[adding toppings]", (){
    test('updates the pizza state accordingly', (){

      schedule(()=> xPizza.addWholeTopping('green peppers'));

      schedule((){
        expect(xPizza.wholeToppings, contains('green peppers'));
        expect(xPizza.firstHalfToppings, isNull);
        expect(xPizza.secondHalfToppings, isNull);
      });
    });
  });
Which does the trick nicely:
PASS: [defaults] it has no toppings
PASS: [adding toppings] updates the pizza state accordingly
All 2 tests passed.
Dang it. I was so close last night. If I had only guessed the right thing to investigate, I could have performed a little test driven development on my new model-driven attributes. Ah well, there is still time for that. But first, I will investigate how @initMethod should be used in Polymer.dart 0.9. I never really expected it to have an effect on my code. Since it does, it must be used for something in 0.9. If that something is similar to the Polymer test setup that I had to do in 0.10, then I believe that I have some test re-organization that I can undertake.

Tomorrow.


Day #80

No comments:

Post a Comment