Thursday, October 31, 2013

Beautiful Acceptance Tests in Angular.dart


I ended yesterday with an Angular.dart acceptance test that looks like:
    test('Retrieves records from HTTP backend', (){
      inject((TestBed tb, HttpBackend http) {
        // 1. Stub HTTP responses
        http.
          whenGET('/appointments').
          respond(200, '[{"id":"42", "title":"Test Appt #1", "time":"00:00"}]');

        // 2. Apply all Angular directive to supplied HTML
        tb.compile(HTML);

        // 3. Wait a tick, then execute the first response
        Timer.run(expectAsync0(() {
          http.responses[0]();

          // 4. Wait a tick, then “digest” things
          Timer.run(expectAsync0(() {
            tb.rootScope.$digest();

            // 5. Expect that the test appointment is in the DOM now
            var element = tb.rootElement.query('ul');
            expect(element.text, contains('Test Appt #1'));
          }));
        }));
      });
    });
That is not too bad, but I think there is room for improvement.

I start with #3 and “executing” the response. That struck me as odd, but while I was digging through the underbelly of Angular.dart's MockHttpBackend, it seemed necessary. I found that the callbacks that would trigger Future completion in my application code were stored in these responses and so I manually invoked them. Having some time to review, I now see that this is what flush() does.

In fact, I found it odd that I could not get flush() to work previously. I gave up on it when all that it gave me was “No pending request to flush !” errors. It turns out that the “Wait a tick” was the important piece necessary to allow the HTTP responses to be in a position to be flushed. So I change #4 above to:
        Timer.run(expectAsync0(() {
          http.flush();
The other thing that I found off-putting last night was the call to $digest() on the root scope of the module. After digging through the Angular.dart library a bit today, I believe that is necessary. It manually forces bound variables (like the list of appointments) to trigger view updates. Angular.dart has a moderately complex algorithm to determine when to do this in a live application. Invoking $digest() in test speeds things along.

So flush() and $digest() are (I think) legitimate things in an Angular.dart acceptance test. The one other thing that I do to improve the test is to call in the scheduled_test library. I update the development dependencies in pubspec.yaml:
name: angular_calendar
dependencies:
  angular: any
  # ...
dev_dependencies:
  scheduled_test: any
Then, I pub get the new dependency.

The scheduled_test package is unittest with some nice asynchronous support. The only incompatibility with unittest is the lack of a tearDown(). Instead of a tearDown(), I have to schedule what to do when the current test is complete. In this test, that means that I need to tear down the test injector:
    setUp((){
      setUpInjector();
      module((Module _) => _
        ..type(MockHttpBackend)
        ..type(AppointmentBackend)
        ..type(AppointmentController)
        ..type(TestBed)
      );

      currentSchedule.onComplete.schedule(tearDownInjector);
    });
The rest of the setUp() is exactly as it had been before—setting things up for Angular.dart dependency injection in test, injecting my application controller and backend service, along with a mock HTTP service and test bed.

As for the the test itself, I can schedule asynchronous tasks, which will be executed in serial, one-per “schedule.” This ends up looking like:
    test('Retrieves records from HTTP backend', 
      inject((TestBed tb, HttpBackend http) {
        http.
          whenGET('/appointments').
          respond(200, '[{"id":"42", "title":"Test Appt #1", "time":"00:00"}]');

        tb.compile(HTML);

        schedule(()=> http.flush());
        schedule(()=> tb.rootScope.$digest());
        schedule((){
          expect(
            tb.rootElement.query('ul').text,
            contains('Test Appt #1')
          );
        });
      })
    );
That… is very pretty.

The http.whenGET() and tb.compile() could just as easily move into another setUp(). Even here, they are so compact that they hardly seem out of place.

After the setup, I schedule the http.flush() and scope.$digest() that I determined were necessary Angular.dart things under test. The last schedule is to set my expectation. Compact. Easy to read. Full browser stack, acceptance test. I like it!


Day #921

Wednesday, October 30, 2013

An Ugly (But Legit) Angular.dart Acceptance Test


In my angular-calendar example application, a controller fetches records from the server, then populates the view. The then part of that statement is important tonight. It is important because the then() in my Angular.dart application code is not working in my tests.

The AppointmentController is backed by an AppointmentBackend service. When the controller is created, it calls the init() method in the HTTP backend service to fetch existing records:
class AppointmentBackend {
  // ...
  init(AppointmentController cal) {
    _http(method: 'GET', url: '/appointments').
      then((HttpResponse res)=> cal.appointments = res.data);
  }
}
This works fine in the application:



But I have been unable to get the then() in the service's init() to fire under test:
    test('Retrieves records from HTTP backend', (){
      inject((TestBed tb, HttpBackend http) {
        http.
          whenGET('/appointments').
          respond(200, '[{"id:"42", "title":"Test Appt #1", "time":"00:00"}]');
        tb.compile(/* HTML template here */);
        var element = tb.rootElement.query('ul');
        expect(element.text, contains('Test Appt #1'));
      });
    });
This test stubs out HTTP GET requests to /appointments, responding with the "Test Appt #1" appointment. The test bed, which is an Angular.dart thing, should compile HTML, attach controllers and do any normal initialization. And it seems to work as I expect, but only to a point. If the HTTP stub is not present, I get an error. But with it present, I am not seeing the necessary then() callback in the application code. The test fails with:
ERROR: Appointment Application Retrieves records from HTTP backend
  Test failed: Caught Expected: contains 'Test Appt #1'
    Actual: '\n'
    '        \n'
    '      '
But even if I put tracer bullets in the then() callback, it is not being called.

A pub upgrade to the latest version similarly has no effect:
➜  angular-calendar git:(master) ✗ pub upgrade
Resolving dependencies.............................................
Downloading angular 0.0.7 from hosted...
Dependencies upgraded!
So at this point either the testing library is not working properly or it works differently than I expect it should work. Even though this is pre-alpha software, it is more than likely that my understanding is incorrect. Regardless, the approach to getting the test to behave as desired is the same—I need to start poking around in the actual Angular.dart library.

This is Dart, so I need only point my sample application's pubspec.yaml to my local copy of the GitHub repo:
name: angular_calendar
dependencies:
  angular:
    path: ../angular.dart
# ...
And, with a quick pub get to obtain the new dependency, I am ready.

And, eventually, I figure it out. At some point, I ought to record a screencast of how I do this—not because I have some awesome skills or approach, but rather because my approach is such a mess. My success is not based on any real skill. It is sheer will and much blind luck.

What I find is that I need to wait for one reactor loop. Twice. After the first loop, I have to manually invoke the mock HTTP service's response callbacks. This seems to be a way to get the futures involved to complete, which then invoke any then() callbacks. The next wait allows the controller time to assign the mock HTTP updated appointments instance variable. I then have to manually “digest” the template to see the result. The test that does all that is:
    test('Retrieves records from HTTP backend', (){
      inject((TestBed tb, HttpBackend http) {
        http.
          whenGET('/appointments').
          respond(200, '[{"id":"42", "title":"Test Appt #1", "time":"00:00"}]');
        tb.compile(/* HTML template here */);

        Timer.run(expectAsync0(() {
          http.responses[0]();

          Timer.run(expectAsync0(() {
            tb.rootScope.$digest();

            var element = tb.rootElement.query('ul');
            expect(element.text, contains('Test Appt #1'));
          }));
        }));
      });
    });
As tests go, that is pretty ugly. I am not a stickler for clean code in tests, so that is not too much of a concern. I can even live with the nested Timer.run() calls (which run the enclosed code on the next reactor loop). I would probably switch to scheduled_tests to clean those up a little, but even as-is, I can live with them.

What makes this ugly is that I reach under the Angular covers. Twice. Calling the first HTTP response just looks weird. I am not even sure what $digest() does—I just found it in some of the internal Angular tests. Hopefully I can find a higher-level way to get the same functionality.

But as ugly as those are, I have a legit acceptance test for my Angular.dart application. That is a fine way to end the day.


Day #920

Tuesday, October 29, 2013

Simple Acceptance Tests in Angular.dart


Up tonight, I hope to test the full stack of my angular-calendar application. Well, maybe not the full stack, but the entire application with some of the HTTP calls stubbed out. It is written in Angular.dart, the Dart port of the exciting AngularJS framework.

Last night, I got unit testing working, thanks to the built-in module() and inject() test helpers. Actually, it was more than unit testing—it was interaction testing. Using the Angular.dart test helpers, I was able to inject two custom written Angular classes and test the interaction of the two. I think that I can build on that to achieve near-acceptance testing.

The actual application module currently injects two application classes—a controller and a backend service:
import 'package:angular/angular.dart';
import 'package:angular_calendar/calendar.dart';
main() {
  var module = new AngularModule()
    ..type(AppointmentBackend)
    ..type(AppointmentController);
  ngBootstrap(module: module);
}
(I have a separate branch with routing, but it is not currently in master)

The unittest setUp() equivalent should be something along the lines of:
  group('Appointment Application', (){
    setUp((){
      setUpInjector();
      module((Module _) => _
        ..type(MockHttpBackend)
        ..type(AppointmentBackend)
        ..type(AppointmentController)
      );
    });
    // Tests will go here...
  });
The built-in setUpInjector() helper method prepares the testing environment for Angular's dependency inject goodness. The module() helper creates a DI module, with some testing support already injected. Then I used the DI package's type() to inject three classes by type: the two application classes and a Angular.dart's MockHttpBackend, which is used to stub out HTTP calls.

That setUp() block works just fine. Except it does not actually do anything. As I did last night, I could use the inject() test helper to inject instances of one or more of those classes into a test. But that won't help me write an acceptance test along the lines of “if the user hits enter in the new appointment text field, it should be added to the UI.” At most I can say that, if the controller's add() method is invoked, then the appointments instance variable should include a new record. That is a very valuable test (and one that I learned how to write last night), but not useful for acceptance tests.

For acceptance tests in Angular.dart, I need a TestBed. Like their AngularJS counterparts, the Angular.dart team is going out of the way to make application testing robust and powerful. The primary tools include the already seen module() and inject() helper method and the new TestBed class. Since the test bed is the thing being testing, it gets inject()-ed after the module() setup:
  group('Appointment Application', (){
    TestBed tb;
    setUp((){
      setUpInjector();
      module((Module _) => _
        ..type(MockHttpBackend)
        ..type(AppointmentBackend)
        ..type(AppointmentController)
      );
      inject((TestBed _) => tb = _);
    });
    // Tests will go here...
  });
Here, it might be better to test my routing branch, because the HTML that needs to be compiled with my Angular module has a smaller footprint. For now, I stick with the same largish template that exists on the application's index.html file. It gets compiled into the test bed in the acceptance test:
    test('Retrieves records from HTTP backend', (){
      tb.compile('''
    <div appt-controller>
      <ul class="unstyled">
        <li ng-repeat="appt in day.appointments">
          {{appt.time}} {{appt.title}}
          <a ng-click="day.remove(appt)">
            <i class="icon-remove" ></i>
          </a>
        </li>
      </ul>
      <form onsubmit="return false;" class="form-inline">
        <input ng-model="day.newAppointmentText" type="text" size="30"
               placeholder="15:00 Learn Dart">
        <input ng-click="day.add()" class="btn-primary" type="submit" value="add">
      </form>
    </div>''');

      // Expectations will go here...
    });
If I run the test as-is, I get a failure:
Uncaught Error: [Unexpected request: GET /appointments
No more requests expected]
Nice! So my test, which does not actually have any expectations, fails because my mock HTTP backend saw an HTTP request that it was not told about. Already I am testing some pretty full-stack stuff: that the controller attaches to this view template and makes an HTTP request to populate the template.

To change the message, I can stub the request with whenGET():
    test('Retrieves records from HTTP backend', (){
      inject((TestBed tb, HttpBackend http) {
        http.
          whenGET('/appointments').
          respond(200, '[{"id:"42", "title":"Test Appt #1", "time":"00:00"}]');

        tb.compile(/* ... */);
      // Expectations will go here...
    });
And that works! The test "passes" because I have successfully stubbed out the initial HTTP requests.

I am not quite done, however. I would like to see the response populate the view. I would hope that something like this would work:
    test('Retrieves records from HTTP backend', (){
      inject((TestBed tb, HttpBackend http) {
        http.
          whenGET('/appointments').
          respond(200, '[{"id:"42", "title":"Test Appt #1", "time":"00:00"}]');
        tb.compile(/* ... */);
        var element = tb.rootElement.query('ul');
        expect(element.text, contains('Test Appt #1'));
      });
    });
But I am unable to get the mock HTTP's future to complete under my test bed. If I try to print from the future in the application code:
class AppointmentBackend {
  Http _http;
  AppointmentBackend(this._http);
  init(AppointmentController cal) {
    return _http(method: 'GET', url: '/appointments').
      then((HttpResponse res) {
        print("res: ${res.responseText}");
        return cal.appointments = res.data;
      });
  }
  // ...
}
I never see the print() statement output. The return type of _http() is a Future—it is just not being completed.

Ah well, fodder for tomorrow. Even without that working fully, this test bed stuff seems extremely promising.


Day #919

Monday, October 28, 2013

Proper module() and inject() Testing in Angular.dart


Some nights can be summed up as: why-wont-it-work, why-wont-it-work, why-wont-it-work, aw-ferget-it… oooh-it-works. Last night's Angular.dart testing was one of those messes that ended the night working, I think. Tonight, I make sure that I have figured out the proper way of testing the Dart port of AngularJS.

With a day to reflect on last night's madness, I realize that understanding Angular.dart and testing Angular.dart means that I need a better handle on the DI dependency injection framework. Specifically, the two most important methods are type(), which injects an object of the specified type into the module, and value(), which injects an already defined object into the module.

Armed with those two bits of trivial, I revisit my test setUp() from yesterday:
  group('Appointment Backend', (){
    var http_backend;
    setUp(() {
      setUpInjector();
      module((Module module) {
        http_backend = new MockHttpBackend();
        module
          ..value(HttpBackend, http_backend)
          ..type(AppointmentBackend);
      });
    });
    // Tests go here...
  });
The setUpInjector() function needs to be called before each test, which is what the setUp() block does in Dart unittest. As the name suggests, setUpInjector() establishes a mock Angular module with support for injecting things under test with the help of two other helper methods: module() and inject().

The inject() and module() helpers are very similar. The stated difference between the two is that module() is for injecting types whereas inject() is for getting an injector back so that tests can operate on an injected instance.

At first, the distinction seems somewhat subtle. Inside the module() call, I create an instance of MockHttpBackend(), which I then inject into the mock angular module with the value() method, which injects instances rather than types:
      module((Module module) {
        http_backend = new MockHttpBackend();
        module
          ..value(HttpBackend, http_backend)
          ..type(AppointmentBackend);
      });
I am injecting an instance (http_backend) and a type (AppointmentBackend, the module actually being tested) thanks to DI's value() and type(). So the distinction between module() and inject() is not that instances of objects can be used, it is how they are used.

The module() function parallels the normal DI module() function, building up a series of modules and types are injected. The inject() method then injects one last module to grab instances of injected classes for testing. The mock Angular module has an instance of my AppointmentBackend—to test it, I inject one last object to retrieve that instance for testing:
    test('add will POST for persistence', (){
      inject((AppointmentBackend server) {
        http_backend.
          expectPOST('/appointments', '{"foo":42}').
          respond('{"id:"1", "foo":42}');

        server.add({'foo': 42});
      });
    });
In that sense, the anonymous Dart function that is being injected behaves like a DI class—the function is in turn injected with the types specified in the argument list.

Now that I think about it, there is no need for me to muck with the instance of MockHttpBackend in the setUp() block. I can inject it into the module by type and then retrieve it in the inject() function:
  group('Appointment Backend', (){
    setUp(() {
      setUpInjector();
      module((Module _) => _
        ..type(MockHttpBackend)
        ..type(AppointmentBackend)
      );
    });

    test('add will POST for persistence', (){
      inject((AppointmentBackend server, HttpBackend http) {
        http.
          expectPOST('/appointments', '{"foo":42}').
          respond('{"id:"1", "foo":42}');

        server.add({'foo': 42});
      });
    });
That is a nice, concise test. Given a module with an Http service and an AppointmentBackend, I expect the described POST if I add a record. It is not quite end-to-end, but it is very, very powerful.

Speaking of non-end-to-end tests, I have another pre-exiting test that sets expectations on the interaction between the AppointmentController and the AppointmentBackend (the latter of which is tested above against Http). Since these are unit tests, I wanted to verify that an action that triggers an add() in the UI controller triggered an add() in the backend service. For that, I injected a mock backend that could keep track of the number of times a particular method was called.

This is a good use case for value() in an Angular.dart setUp() block:
  group('Appointment controller', (){
    setUp((){
      setUpInjector();
      var server = new AppointmentBackendMock();
      module((Module _) => _
        ..value(AppointmentBackend, server)
        ..type(AppointmentController)
      );
    });
    // Tests here...
  });
I cannot just inject the AppointmentBackMock because the class definition is too spartan:
class AppointmentBackendMock extends Mock implements AppointmentBackend {}
But I can use value() to tell my test module that it should be injected as an AppointmentBackend.

The test can then inject() the mock server and the controller so that is can invoke the add() method on the controller and check the expectation that the method of the same name was called on the mock server:
  group('Appointment controller', (){
    setUp((){
      setUpInjector();
      var server = new AppointmentBackendMock();
      module((Module _) => _
        ..value(AppointmentBackend, server)
        ..type(AppointmentController)
      );
    });
    tearDown(tearDownInjector);

    test('adding records to server', (){
      inject((AppointmentController controller, AppointmentBackend server) {
        controller.newAppointmentText = '00:00 Test!';
        controller.add();

        server.
          getLogs(callsTo('add', {'time': '00:00', 'title': 'Test!'})).
          verify(happenedOnce);
      });
    });
  });
After rewriting a few more tests, I have the entire test suite passing again—now with a more Angular feel and usage. I still believe that opportunities exist for getting closer to end-to-end acceptance testing with this approach. I will cover that tomorrow. But even this minimal interaction testing is concise-yet-powerful. I am already very excited to start using it more.


Day #918

Sunday, October 27, 2013

Getting Started with Angular.dart's New Testing Features


I probably need to quit while I am ahead with Angular.dart. It is under such active development that I could spend every other night correcting posts that have been made obsolete by new releases. Between the API documentation and the Angular.dart Tutorial, I am very likely polluting the documentation pool with stuff that will be obsolete before long.

Still, it is very exciting that so much is happening with the Dart port of the AngularJS project. I fully intend to come back at some point and before I leave, I cannot resist the chance to play with the test bed that the project uses internally. Yes, I am completely obsessed with testing and, more importantly, finding new and unique ways to test.

Happily testing methods were recently exposed directly in the library, so there is no need for me to do bad things to my local copy of the repository. All of this work is done against the 0.0.6 release of the package.

I start with my test setup, which needs to establish the injector context for the test:
import 'package:unittest/unittest.dart';
import 'package:unittest/mock.dart';
import 'dart:html';
import 'dart:async';

import 'package:angular/angular.dart';
import 'package:angular/mock/module.dart';

import 'package:angular_calendar/calendar.dart';

main(){
  group('Appointment', (){
    setUp(setUpInjector);
    // ...
  });
}
The setUpInjector function is part of the angular mock library, which is already being imported.

Next, I need a module that includes a mock HTTP backend service so that I can set expectation:
    setUp(module((Module module) {
      http_backend = new MockHttpBackend();
      module
        ..value(HttpBackend, http_backend);
    }));
The module() function enables me to inject a fake HTTP backend into the test so that I do not need a real server running in the background. With the setup out of the way, I ought to be able to inject an instance of my backend service for actual testing.

And here, I am a bit stumped. I have a mock Angular module that has already injected a mock Http instance. I want to inject my backend into the test, so I try:
    test('add will POST for persistence', (){
      inject((AppointmentBackend b) { server = b; });

      http_backend.
        expectPOST('/appointments', '{"foo":42}').
        respond('{"id:"1", "foo":42}');

      server.add({'foo': 42});
    });
But that does not work. When I run the test, I get:
ERROR: Appointment Backend add will POST for persistence
  Test failed: Caught Illegal argument(s): No provider found for AppointmentBackend! (resolving AppointmentBackend) at position 0 source:
   (AppointmentBackend b) { server = b; }.
  #0      DynamicInjector.invoke.<anonymous closure> (package:di/dynamic_injector.dart:197:9)
  #1      DynamicInjector.invoke.<anonymous closure> (package:di/dynamic_injector.dart:196:9)
I am not quite sure what this means since I am importing my application code that defines this class. Ah well, I will keep at this tomorrow and hopefully figure out what I am doing wrong.

Update: Figured it out. This error is telling me that I have not injected the class into the fake Angular module that I am trying to use. I need only type(AppontmentBackend) to get the code working. The full version of the test using the new test module() and inject() is then:
  group('Appointment Backend', (){
    var server, http_backend;
    setUp(() {
      setUpInjector();

      module((Module module) {
        http_backend = new MockHttpBackend();
        module
          ..value(HttpBackend, http_backend)
          ..type(AppointmentBackend);
      });
    });

    test('add will POST for persistence', (){
      inject((AppointmentBackend b) { server = b; });

      http_backend.
        expectPOST('/appointments', '{"foo":42}').
        respond('{"id:"1", "foo":42}');

      server.add({'foo': 42});
    });
  });
I am still not sure I have the hang of this, so I will likely follow up more tomorrow.


Day #917

Saturday, October 26, 2013

Routing Without Scope in Angular.dart


Scope is the devil's plaything.

Scope is defined as the range on one's perception. In programming, scope is the context in which an identifier is available. In the real world, it is generally considered a good thing to have broad scope—the wider a perspective, the more informed a decision. More information in programming scope is bad. The more identifiers that can influence a particular decision in code, the greater the chance that an unrelated change somewhere else can introduce bugs. “Sure I updated the params in that class, it fixed a bug… Oh, the same params object is used over there for that case? Ugh.”

So the less information available to code, the better. This is one of the things that makes AngularJS so appealing. By default, nothing is available to the various templates, components, controllers, and routes. Developers have to explicitly inject objects that are needed for each. Each of the objects that can be injected is responsible for just one thing (route parameters, server interaction, form control). Between dependency injection and single responsibility objects, AngularJS tends toward exceptionally clean code. Except for scope.

Scope in AngularJS is mostly intended to sync controller variables for use in views. But it is too easy to become a dumping ground for things that ought to get their own containing classes. This is one of the reasons that I really like the Angular.dart approach to controllers, which use instance properties rather than scope.

And yet, scope still exists in Angular.dart. And, to my shame, I dumped stuff in there last night. Unable to figure out the right way to make route information available to my controllers, I added it to scope:
class CalendarRouter implements RouteInitializer {
  Scope _scope;
  CalendarRouter(this._scope);

  void init(Router router, ViewFactory view) {
    router.root
      // ...
      ..addRoute(
          name: 'day-view',
          path: '/days/:dayId',
          enter: (RouteEvent e) {
            e.route.parameters.forEach((k,v) { _scope[k] = v; });
            return view('partials/day_view.html')(e);
          }
        );
  }
}
So now anything in my application has access to these parameters via scope. And if I ever make a change, anything in my application that relies on scope can break.

Fortunately, thanks to Artem Andreev, I think I understand the way Angular.dart want this done. Taking look back at what did not work yesterday, I was attempting to directly inject a RouteProvider into my controller:
@NgDirective(
  selector: '[day-view-controller]',
  publishAs: 'appt'
)
class DayViewController {
  String id;
  String title = 'Default Title';
  String time = '08:00';
  AppointmentBackend _server;

  DayViewController(RouteProvider router, this._server) {
    id = router.route.parameters["dayId"];
    _server.
      get(id).
      then((rec) {
        title = rec['title'];
        time = rec['time'];
      });
  }
}
As both Artem and the documentation pointed out, if an attempt is made to inject a RouteProvider without a ng-bind-route directive in the HTML, the RouteProvider would always be null.

The day-view route specifies that the partials/day_view.html view be used, so I wrap the content inside an ng-bind-route directive:
<div ng-bind-route="day-view">
<div appt-controller>
  <!-- normal controller template stuff here -->
</div>
</div>
I do not understand the need for the ng-bind-route here. The routing has already bound the route to the view, this reciprocation seems an awful lot like duplication. Hopefully someday only one declaration of the relationship will be needed.

With that in place, I am back to where I spent much of yesterday: obscure dependency injection land. Specifically, I am getting:
NoSuchMethodError : method not found: 'simpleName'
Receiver: null
Arguments: []
There is also a 131 line stack trace which I choose to omit here for sanity's sake.

It turns out that last piece of the puzzle that I have been missing was a simple import. Most of the calendar related code resides in calendar.dart, which is separate from the Angular module and routing code in main.dart. And I simply forgot to import Angular routing into calendar.dart as I had already done in main.dart. With the fix in place:
import 'package:angular/angular.dart';
import 'package:angular/routing/module.dart';
import 'dart:convert';
@NgDirective(
  selector: '[day-view-controller]',
  publishAs: 'appt'
)
class DayViewController {
  // ...
}
I now have routing parameters working. The dayId is sent in via the RouteProvider, which I can use to retrieve the appointment information from the server, which then populates the template:



That is not quite the end of routing in Angular.dart. It seems the RouteHandle returned from router.route does a fair bit of scope work itself. So much so that it will stick around even if the route is no longer active. To get rid of it, I have to call its discard method. The time to discard a route is when a “detach aware” object is detached.

In other words, my controller needs to implement NgDetachAware and define a detach() method to discard the route:
@NgDirective(
  selector: '[day-view-controller]',
  publishAs: 'appt'
)
class DayViewController implements NgDetachAware {
  AppointmentBackend _server;
  RouteHandle route;

  DayViewController(RouteProvider router, this._server) {
    route = router.route;
    // ...
  }

  detach() {
    // The route handle must be discarded.
    route.discard();
  }
}
A class that implements NgDetachAware will have its detach() method invoked whenever it is no longer active—the perfect time to discard a route handle.

I think that covers the basics of routing in Angular.dart. There are still other topics (pushState, nested routes) that I may or may not investigate deeper, but I tend to expect that most will be fairly straight-forward now that I understand this. I am still skeptical about the need to to reciprocate the route-view binding. Given the ceremony involved with NgDetachAware, I wonder if my scope-based approach might have some merit after all. All the more so if RouteHandle has its own scope baggage.

But I will leave those questions for another day. For now, I am finally glad to have solved the puzzle of properly injecting routes into controllers.


Day #916

Friday, October 25, 2013

Really Stupid Routing Parameters in Angular.dart


Whenever something is really, really hard to do in a library, I am almost certainly doing something really stupid. I love when that happens.

I love it because there is almost certainly something fundamental that I am missing. Something that the library wants me to do in a certain way. And if I can figure out that way, then learning starts. More importantly, the library starts teaching me why things are done that way. It's the why that I really love. The day that I learn why a framework has me do something a particular way is always a good day. Today is not one of those days. Today, I struggle through a really dumb solution—to Angular.dart routing.

I have routing working with the master branch of Angular.dart (which is the same as version 0.0.5). The default route in my calendar application points to a controller-backed list view of all appointments. A separate route points to a controller-backed individual appointment detail view. The routing works fine. The controllers attached to the two views work fine.

The problem is that I cannot figure out how to get the dayId parameter in the route sent to the appointment detail controller. From last night, routing in in Angular.dart is currently implemented by the route_hierarchical package. In my calendar application, this looks like:
class CalendarRouter implements RouteInitializer {
  void init(Router router, ViewFactory view) {
    router.root
      ..addRoute(
          defaultRoute: true,
          name: 'day-list',
          enter: view('partials/day_list.html')
        )
      ..addRoute(
          name: 'day-view',
          path: '/days/:dayId',
          enter: view('partials/day_view.html')
        );
  }
}
By virtue of the section route, I ought to be able to access /days/42 URLs and the detail view controller should know to lookup record ID=42 from the backend via the dayId parameter.

Now, I know that there must be a very easy way to do this in Angular.dart, but after some time of searching and injecting and searching and injecting some more, I have come up empty. I know that, ultimately, I need a Route object from the route_hierarchical package so that I can get access to the parameters property. But I do not see how to get that object in the controller.

From the route_hierarchical documentation, I know that the enter named parameter to the addRoute() is a function that receives a RouteEvent object. The RouteEvent object has a route property. Perhaps that can help...

The view() that is currently being assigned to the enter named parameter is returning a function that takes RouteEvent object as an argument. I still need to call that to get my Angular.dart routing working properly, but I also need to get that route. This winds up fitting the bill:
class CalendarRouter implements RouteInitializer {
  void init(Router router, ViewFactory view) {
    router.root
      // ...
      ..addRoute(
          name: 'day-view',
          path: '/days/:dayId',
          enter: (RouteEvent e) {
            print(e.route.parameters);
            return view('partials/day_view.html')(e);
          }
        );
  }
}
Now when I access the day view route, I get the parameters dumped to the console:
{dayId: a7380ec0-17e0-11e3-a775-774b90ad8e75}
That is only part of the battle. I have the dayId paramter in the route, but I need to get it to the controller somehow.

And the best that I can think to do in this case is to inject scope into my router and then dump the parameters into scope:
class CalendarRouter implements RouteInitializer {
  Scope _scope;
  CalendarRouter(this._scope);

  void init(Router router, ViewFactory view) {
    router.root
      // ...
      ..addRoute(
          name: 'day-view',
          path: '/days/:dayId',
          enter: (RouteEvent e) {
            e.route.parameters.forEach((k,v) { _scope[k] = v; });
            return view('partials/day_view.html')(e);
          }
        );
  }
}
After injecting scope into the day view controller, I finally have access to that parameter:
@NgDirective(
  selector: '[day-view-controller]',
  publishAs: 'appt'
)
class DayViewController {
  // ...
  DayViewController(Scope scope) {
    print('dayId: ${scope["dayId"]}');
  }
}
And, when I load the page, I see my UUID dayId value logged to the console:
dayId: a7380ec0-17e0-11e3-a775-774b90ad8e75
So I have routing parameters working. But communicating through scope feels wrong. In plain-old AngularJS, it is possible to inject route parameters into objects. Angular.dart must have something similar—or some alternate approach that I am missing. Or perhaps it is still a feature in need of development?

Regardless, I have a really dumb routing parameters solution. And the promise of something better in the near future.

Day #915

Thursday, October 24, 2013

Routing in Angular.dart


I can resist no longer. Today: routing in Angular.dart. I don't know that routing in AngularJS is any more interesting than it is in other MV-whatever libraries and frameworks in JavaScript land, so I have no extra expectations with it in the Dart port. Still, routing is just cool, so today I have a go.

To date, I have been able to accomplish all of my Angular.dart exploration with the most recent Pub package (version 0.0.2 at the time of this writing). That lacks routing, but happily Dart makes it trivial to work with local repository versions of libraries.

I have already cloned the repository:
git clone https://github.com/angular/angular.dart.git
So I only need to pull down the latest commits. Nothing needs to happen to the repository to make it usable as a package. Back in my angular-calendar repo, I update pubspec.yaml to point to the local copy of Angular.dart (both reside in the same $HOME/repo directory):
name: angular_calendar
dependencies:
  angular:
    path: ../
  # Server dependencies
  dirty: any
  uuid: any
  ansicolor: any
dev_dependencies:
  unittest: any
I pub upgrade to upgrade to the most recent versions of dependencies—including any that the master branch of Angular.dart now require.

I have to update the my main.dart entry point to use the more angular-y ngBootstrap (instead of bootstrapAngular()):
import 'package:angular/angular.dart';
import 'package:angular_calendar/calendar.dart';
main() {
  var calendar = new AngularModule()
    ..type(AppointmentBackend)
    ..type(AppointmentController);

  ngBootstrap(module: calendar);
}
With the preliminaries out of the way, I need to inject a RouteInitializer into my Angular module. This is accomplished with the same type() method that all di.dart modules use, but with a slight twist. I pass the vanilla RouteInitializer class and then tell type() that it is implemented by my custom router class (that I need to write next):
import 'package:angular/angular.dart';
import 'package:angular/routing/module.dart';
import 'package:angular_calendar/calendar.dart';
main() {
  var calendar = new AngularModule()
    ..type(AppointmentBackend)
    ..type(AppointmentController)
    ..type(RouteInitializer, implementedBy: CalendarRouter);

  ngBootstrap(module: calendar);
}
I have also imported Angular routing to get this working. And yes, the implementedBy named parameter seems to be required.

As for the CalendarRouter, it does implement RouteInitializer, which mandates that I build my routes in an init() method:
class CalendarRouter implements RouteInitializer {
  void init(Router router, ViewFactory view) {
    router.root
      ..addRoute(
          defaultRoute: true,
          name: 'day-list',
          enter: view('partials/day_list.html')
        )
      ..addRoute(
          name: 'day-view',
          path: '/days/:dayId',
          enter: view('partials/day_view.html')
        );
  }
}
The router and view that are injected into my router are used to add routes to the application and associate views to each. The default route will enter the partials/day_list.html view. A route of something like /days/42 will enter the partials/day_view.html view (with the dayId parameter assigned.

The easiest way to get routing working is to change my entry page to include the <ng-view> tag, which turns routing on:
<!doctype html>
<html ng-app>
  <head>
    <title>Angular Calendar</title>
    <script type="application/dart" src="main.dart"></script>
    <script src="packages/browser/dart.js"></script>
    <link href="css/bootstrap.min.css" rel="stylesheet" media="screen">
  </head>
  <body>
    <div class="container">
    <h2>Angular Calendar</h2>

    <ng-view></ng-view>

    </div>
  </body>
</html>
I believe that it is necessary to add a <script> tag to get routing working in AngularJS. That is taken care of by the more appropriate code import in the Dart version of Angular.

With <ng-view> now routing and partials/day_list.html designated as my default route, I need that partial. I copy the controller-bound HTML that used to be in index.html into that file as:
<div appt-controller>
  <ul class="unstyled">
    <li ng-repeat="appt in day.appointments">
      <a href="/days/{{appt.id}}">{{appt.time}} {{appt.title}}</a>
      <a ng-click="day.remove(appt)">
        <i class="icon-remove" ></i>
      </a>
    </li>
  </ul>
  <form onsubmit="return false;" class="form-inline">
    <input ng-model="day.newAppointmentText" type="text" size="30"
           placeholder="15:00 Learn Dart">
    <input ng-click="day.add()" class="btn-primary" type="submit" value="add">
  </form>
</div>
That is still bound to the AppointmentController by virtue of the appt-controller selector on the containing <div> and the @NgDirective that is still on the controller:
@NgDirective(
  selector: '[appt-controller]',
  publishAs: 'day'
)
class AppointmentController {
  // ...
}
And that works! When I load up my application, I still get my day view of appointments:



I have linked the individual appointments to <a href="/days/{{appt.id}}">{{appt.time}} {{appt.title}}</a>, which ought to work with the day-view route that I created above. And it does. I define the partials/day_view.html template as:
<div day-view-controller>
  <dl class="dl-horizontal">
    <dt>Title</dt>
    <dd>{{appt.title}}</dd>
    <dt>Time</dt>
    <dd>{{appt.time}}</dd>
  </dl>
</div>
And inject a new DayViewController into my Angular module:
main() {
  var calendar = new AngularModule()
    ..type(AppointmentBackend)
    ..type(AppointmentController)
    ..type(DayViewController)
    ..type(RouteInitializer, implementedBy: CalendarRouter);

  ngBootstrap(module: calendar);
}
Lastly, I define a skeleton of that controller:
@NgDirective(
  selector: '[day-view-controller]',
  publishAs: 'appt'
)
class DayViewController {
  String title = 'Default Title';
  String time = '08:00';
}
And I have the route working:



It took a while for my to figure out the addRoute() format, but I think it makes pretty decent sense. I still rather miss the when() routes from AngularJS, but it is way to early to have a real opinion. I have been unable to figure out how to get routing parameters injected into my controllers (e.g. the dayId value), so I will pick up there tomorrow. There also seem to be alternate ways to establish routes that I may explore as well.

But all in all, routing in Angular.dart has not disappointed so far!


Day #914

Wednesday, October 23, 2013

Sanity Checking Angular.dart Application Code


I have played a little fast and loose with my Angular.dart code. Before moving on, I really need to sanity check my progress.

Sanity checks come in three main forms for me when working with new code. First, and probably most important, is better naming. As I better understand my domain and tools, I refine the names of classes and variables. Next, I try to reproduce some of my work so that I can have some confidence that I am not programming by coincidence. Lastly, I try to refactor some code to validate that my code is maintainable—that tests continue to pass and smoke tests don't hint at fire.

I start by renaming my AppointmentCtrl class as AppointmentController. The “Ctrl” abbreviation for controller is an AngularJS convention. I prefer full names for readability. It is a minor thing, but I keep finding my fingers typing the whole word, so I give in here. More importantly, I rename the ServerController class as AppointmentBackend. It is not a controller, which would be associated with an ng-controller directive. It is an HTTP backend wrapper.

With those changes, my module injection is now:
  var module = new AngularModule()
    ..type(AppointmentBackend)
    ..type(AppointmentController);

  bootstrapAngular([module]);
(bootstrapAngular() will be renamed ngBootstrap() any day now)

Next up: verify that I am not programming by coincidence. I think that I have a handle on how to build Angular.dart controllers and test them, let's find out!

After various testing, I have need of a remove action for my appointment controller:



In the HTML, I will want to use a remove() method on the controller to implement:
    <div appt-controller>
      <ul class="unstyled">
        <li ng-repeat="appt in day.appointments">
          <span>{{appt.time}} {{appt.title}}</span>
          <a ng-click="day.remove(appt)"><!-- ... --></a>
        </li>
      </ul>
      <!-- ... -->
    </div>
To drive the remove functionality into existence, I write a test. My test will verify that the server will be told to remove the record:
    // This setup already exists
    var server;
    setUp((){
      server = new AppointmentBackendMock();
    });
    test('removing a record removes it from the server', (){
      var controller = new AppointmentController(server);
      controller.remove({'id': '42'});

      server.
        getLogs(callsTo('remove', '42')).
        verify(happenedOnce);
    });
That fails since there is no remove() method in the AppointmentController class:
ERROR: Appointment controller removing a record removes it from the server
  Test failed: Caught Class 'AppointmentController' has no instance method 'remove'.
  
  NoSuchMethodError : method not found: 'remove'
  Receiver: Instance of 'AppointmentController'
  Arguments: ["42"]
  dart:core-patch/object_patch.dart                                                                                               Object.noSuchMethod
  ../test.dart 46:24     
I make the test pass with:
class AppointmentController {
  // ...
  void remove(String id) {
    _server.remove(id);
  }
  // ...
}
Next, I do the same for the AppointmentBackend. The test, using the Http setup from last night:
  group('Appointment Backend', (){
    var server, http_backend;
    setUp((){
      http_backend = new MockHttpBackend();
      var http = new Http(/* ... */);
      server = new AppointmentBackend(http);
    });
    test('remove will DELETE record', (){
      http_backend.
        expectDELETE('/appointments/42').
        respond('{}');

      server.remove('42');
    });
  });
And the code that makes that test pass:
class AppointmentBackend {
  Http _http;
  AppointmentBackend(this._http);
  remove(String id) {
    _http(method: 'DELETE', url: '/appointments/${id}');
  }
}
And, with the ng-click directive invoking the controller's remove() method, and the controller's remove() method invoking the backend's remove() method, which DELETEs the record from the backend, I am able to remove those old records:



That is actually fairly encouraging. I have been burned before when I tried to add a second test of the same class or object (due to unintended class coupling). With 5 passing tests and the ability to create an remove records, I think I will call it a night here and will pick back up with refactoring tomorrow.



Day #913

Tuesday, October 22, 2013

Testing Angular.dart HTTP Requests


It is possible that the only reason that I have been exploring Angular.dart is so that I could write tonight's post on testing browser HTTP requests in it. Actually, it's not really possible—Angular.dart, like its older JavaScript sibling AngularJS, is shaping up to be a very exciting framework for the browser (and mobile). So any time exploring it, is time well spent. But I am exceedingly interested in how Angular.dart does HTTP testing from the browser.

I have tried mocking Dart's HttpRequest object before, without satisfying results. Oh, it worked, but the code that injected either the real or mock HttpRequest object always ended up feeling a bit forced—like something written solely to satisfy testing, not to generate real code. I am unsure if Angular.dart's mock HTTP request will feel much better in my code—it may be something that works best in a library that lives and breathes dependency injection. Either way, I am going to find out.

I continue to work with my exploratory angular-calendar application. After last night, I have the appointment controller's add() method calling the method of the same name in the server controller:
class AppointmentCtrl {
  List appointments = [];
  String newAppointmentText;
  ServerController _server;

  AppointmentCtrl(this._server) {
    _server.init(this);
  }

  void add() {
    var newAppt = fromText(newAppointmentText);
    appointments.add(newAppt);
    newAppointmentText = null;
    _server.add(newAppt);
  }
  // ...
}
In fact, I have a plain-old Dart unittest that drove this implementation:
    setUp((){
      server = new ServerCtrlMock();
    });

    test('adding records to server', (){
      var controller = new AppointmentCtrl(server);
      controller.newAppointmentText = '00:00 Test!';
      controller.add();

      server.
        getLogs(callsTo('add', {'time': '00:00', 'title': 'Test!'})).
        verify(happenedOnce);
    });
I am using a Dart mock here. The server mock is injected in the appointment controller, which is a typical Angular controller backing various ng-directive type things in a web page. When one of those ng-directive things, specifically an ng-click directive, tells the controller to add an element, it calls the controller's add() method—just like my test does. The test goes on to verify that, by calling the appointment controller add() method, that the server's add() method is called as well, with a Map object to be persisted to the server via a REST call.

It's pretty slick, but there's a problem: the server's add() method isn't actually doing anything yet:
class ServerCtrl {
  Http _http;
  ServerCtrl(this._http);

  init(AppointmentCtrl cal) {
    _http(method: 'GET', url: '/appointments').
      then((HttpResponse res) {
        res.data.forEach((d) {
          cal.appointments.add(d);
        });
      });
  }

  add(Map record) {
    // Something should happen here...
  }
}
I am going to use a little bit of test driven development to make that add() happen.

The ServerCtrl constructor requires a single parameter: an Angular.dart Http object. Since I do not want to actually send a real request in my test (just test what happens if I did), I can not create a real Http object in my test. Instead, I create an instance of MockHttpBackend to supply to my ServerCtrl object:
  group('Server Controller', (){
    var server, http;
    setUp((){
      http = new MockHttpBackend();
      server = new ServerCtrl(http);
    });

    test('dummy', (){ expect(server, isNotNull); });
  });
That works as long as I import the mock module from Angular.dart:
import 'package:unittest/unittest.dart';
import 'package:unittest/mock.dart';
import 'dart:html';
import 'dart:async';

import 'package:angular/angular.dart';
import 'package:angular/mock/module.dart';

import 'package:angular_calendar/calendar.dart';
// ...
So far I have only succeeded in creating an instance of the mock Http class. What I really want to do is set an expectation that a POST request to the /appointments URL should occur, then call the server controller's add() method to check that expectation. Given AngularJS's awesome test support, it should come as no surprise that MockHttpBackend sports a pretty brilliant expectation API. Here, since I expect a POST, I use the expectPOST method:
  group('Server Controller', (){
    var server, http;
    setUp((){ /* ... */ });
    test('add will POST for persistence', (){
      http.expectPOST('/appointments');
      server.add({'foo': 42});
      http.flush();
    });
  });
Like most (all?) HTTP stubbing libraries, MockHttpBackend will store all of the requests that need to be made (e.g. the POST in response to the add() call) so that multiple requests can be tested. Once the test is ready to proceed, tests like this need to flush() all pending requests to make them actually happen.

When I run this test, I get a failure because I have no requests pending:
ERROR: Server Controller add will POST for persistence
  Test failed: Caught [No pending request to flush !]
  package:angular/mock/http_backend.dart 492:28                                                                                MockHttpBackend.flush
  ../test.dart 59:17                                                                                                           main.<fn>.<fn>
  package:unittest/src/test_case.dart 111:30
...
But this is the very code that I am trying to TDD here!

So I try adding code to make my test pass. In my server controller, I call the injected _http object:
class ServerCtrl {
  // ...
  add(Map record) {
    _http(method: 'POST', url: '/appointments');
  }
}
Now, when I run my test, I find that I have changed the error message:
ERROR: Server Controller add will POST for persistence
  Test failed: Caught Closure call with mismatched arguments: function 'call'
  
  NoSuchMethodError: incorrect number of arguments passed to method named 'call'
  Receiver: Instance of 'MockHttpBackend'
  Tried calling: call(method: "POST", url: "/appointments")
  Found: call(method, url, data, callback, headers, timeout)
  dart:core-patch/object_patch.dart                                                                                            Object.noSuchMethod
  package:angular_calendar/calendar.dart 69:10                                                                                 ServerCtrl.add
  ../test.dart 66:17 
Bother.

So it seems that Angular.dart's MockHttpBackend call has a different method signature than Http. I think that's a bug (I promise to file an issue tomorrow if true), but I don't think it is a show-stopper. Instead of injecting a MockHttpBackend into my server controller, I can inject an Http that has a MockHttpBackend instead of the usual HttpBackend:
  group('Server Controller', (){
    var server, http_backend;
    setUp((){
      http_backend = new MockHttpBackend();
      var http = new Http(
        new UrlRewriter(),
        http_backend,
        new HttpDefaults(new HttpDefaultHeaders()),
        new HttpInterceptors()
      );
      server = new ServerCtrl(http);
    });
    test('add will POST for persistence', (){ /* ... */ });
  });
That makes for an uglier test, but I have never been one to care too much about pretty tests. As long as the resulting code is clean.

With that change, and removing the flush() that is no longer necessary, I have my test passing, but complaining. It seems that even when I do not care about the response, I need a response:
    test('add will POST for persistence', (){
      http_backend.
        expectPOST('/appointments').
        respond('{"id:"1", "foo":42}');

      server.add({'foo': 42});
    });
Now I have a passing test with no complaints. I can make one more small improvement. In the expectPOST() call, I can specify that I expect the JSON encoded version of the Map being added to the list of calendar appointments:
    test('add will POST for persistence', (){
      http_backend.
        expectPOST('/appointments', '{"foo":42}').
        respond('{"id:"1", "foo":42}');

      server.add({'foo': 42});
    });
To make that pass, I supply a call to JSON.decode() as the data for my POST in the application code:
class ServerCtrl {
  Http _http;
  ServerCtrl(this._http);
  // ...
  add(Map record) {
    _http(method: 'POST', url: '/appointments', data: JSON.encode(record));
  }
}
With that, I have my calendar add-appointment code working and tested. Yay!

Aside from the little call signature mismatch, this was pretty easy. I am unsure if I can make good use of this in other Dart tests (I'll need to play around with it some more), but I am darned excited about this for testing Angular.dart code.


Day #912

Monday, October 21, 2013

Getting Started with Unit Tests in Angular.dart


I am at least as excited to play with testing in Angular.dart as I am to learn about the cool stuff it has planned for needy web developers. And I am very eager to explore the promise of this Dart port of the very excellent AngularJS. But I crave solid testing.

I test for different reasons. I test differently for each of those reasons. But no matter what, I always test.

I use test driven development to build robust applications. If every method is written in response to a test, every method tends to be small, there tend to be fewer of them and coupling between methods and classes tends to be low. Code that tends to have few, small methods with low coupling is better prepared to support new features. It is robust code.

I use acceptance tests to ensure that my applications are maintainable. Acceptance tests describe high-level features, usually from a human perspective. If there are certain features and workflows that are important to the overall health of the application, acceptance tests exercise these feature as a person might. Acceptance tests maintain by quickly identifying when a change breaks features and workflow.

So far in my exploration of Dart code, I have found no way to write acceptance tests for web applications without running a full-blown web server. This is because there is no way to stub out HTTP requests in Dart (e.g. if this exact request is made, then respond with this data). Dart is a dynamic language, but not so dynamic as to allow runtime modifications to a core class. And so I run actual test servers.

But this is not the only way. In fact, AngularJS uses this other way already to do much of its testing. It injects HTTP request objects, which may or may not be the real thing, into components that need them. I have eschewed this approach in my Dart applications because I do not believe that it provides true acceptance testing. But perhaps the combination of Angular application structure and dependency injection would be closer? If nothing else, I get to see another testing approach, so let's give it a whirl.

I start with a simple test page to provide web context for my tests, test/index.html:
<script type="application/dart" src="test.dart"></script>
<script src="packages/unittest/test_controller.js"></script>
The test_controller.js script in unittest still seems to be a work-in-progress (or I haven't got it 100% sussed), but it establishes a message listener which I can complete by sending a message once all tests have completed. Thus, my skeleton test.dart is:
import 'package:unittest/unittest.dart';
import 'dart:html';

main(){
  pollForDone(testCases);
}

pollForDone(List tests) {
  if (tests.every((t)=> t.isComplete)) {
    window.postMessage('dart-main-done', window.location.href);
    return;
  }

  var wait = new Duration(milliseconds: 100);
  new Timer(wait, ()=> pollForDone(tests));
}
Since many tests are asynchronous, it it necessary to poll like this to ensure that all tests really have completed before telling test_controller.js that testing has completed. Actually, there is probably something in unittest that does this for me—I'll have to find it another day.

I am unsure of the Angular.dart Way™ of testing, so I am going to start small here. I will try to unit test that adding a record in my calendar's appointment controller instructs the server to add a record. The AngularJS Way™ is to inject mock $httpBackend and $controller services (and anything else that is needed). I think I can do that manually (and I'll try that tomorrow). For now, however, I am going to settle for supplying a mock ServerCtrl:
import 'package:unittest/unittest.dart';
import 'package:unittest/mock.dart';
import 'package:angular_calendar/calendar.dart';

class ServerCtrlMock extends Mock implements ServerCtrl {
  Http _http;
  ServerCtrl(Http this._http);
}

main(){
  group('Appointment controller', (){
    var server;
    setUp((){
      server = new ServerCtrlMock();
    });

    test('adding records to server', (){
      var controller = new AppointmentCtrl(server);
      controller.newAppointmentText = '00:00 Test!';
      controller.add();

      server.
        getLogs(callsTo('add', {'time': '00:00', 'title': 'Test!'})).
        verify(happenedOnce);
    });
  });
}
I supply the mock server controller to the AppointmentCtrl constructor. The test then sets the new appointment text, as the Angular view would, then I invoke the add() method, as the ng-click directive in the view would. This is definitely not an acceptance test with all the direct method calls. Still, hopefully this can help drive a small implementation.

The actual expectation in this test is done by checking the logs of the server mock. In this case, I verify that the server's own add() method is invoked once with the string parsed out into the Map shown.

And, after I replace the HttpRequest code in my application:
void add() {
    var newAppt = fromText(newAppointmentText);
    appointments.add(newAppt);
    newAppointmentText = null;
    // HttpRequest.
    //   request(
    //     '/appointments',
    //     method: 'POST',
    //     sendData: JSON.encode(newAppt)
    //   );
    _server.add(newAppt);
  }
My test passes.

Well, that is not quite what I hoped to accomplish tonight, but it is a start. Tomorrow, I am going to fiddle with the mock HTTP backend supplied by Angular.dart. And then maybe I can figure out how to injection is supposed to work in Angular.dart tests.


Day #911

Sunday, October 20, 2013

HTTP Requests in Angular.dart


I have a rough outline of things that I want to explore in Angular.dart. Angular.dart is, of course, the Dart port of AngularJS, the framework that's all the rage. Next on that list is routing, but that is yet to be implemented (it is part of the active milestone, so maybe someday soon). With routing off the table, I move onto the next item on the list: HTTP requests.

The JavaScript version of Angular has a very Dart-like feel to it, thanks to its promises and the $http service that is injected into controllers. So I do not expect too many surprises. That said, I am not quite sure how this will translate the class based approach to controllers that I liked so much last night...

To find out, I start by creating a REST-like backend. I already have one of these for dart-comics, which is the companion code repository for the Dart for Hipsters book. The backend uses a dirt-simple noSQL persistent store, dart dirty. I copy the server from dart-comics to my angular-calendar repository in which I am currently working. I change the resources in the server from “comics” to “appointments” (which means I may want to convert this simple server into a package some day soon).

Anything not in the /appointments URL-space will be served up from my repository's web subdirectory, making this a drop-in replacement for pub serve. After stopping the simple HTTP server built into Dart Pub, I start this server:
➜  angular-calendar git:(master) ✗ dart server.dart
Server started on port: 8000
...
A quick check reveals that my Angular day-view for appointments is still working:



Before I try to use an Angular HTTP service, I think I will try this in plain-old Dart. As I mentioned, AngularJS has a Dart-like feel already—perhaps the HTTP service normally used in Angular applications is not necessary.

In the controller class, I start by removing the default “Wake up” entry that I had been associating with the appointments list. I also create a constructor that will be responsible for loading appointments from my REST-like server:
class AppointmentCtrl {
  List appointments;
  // ...
  AppointmentCtrl() {
    _loadAppointments();
  }
  // ...
}
In the private _loadAppointments() method, I perform a simple HTTP request for the /appointments resource:
  _loadAppointments() {
    HttpRequest.
      getString('/appointments').
      then((responseText){
        appointments = JSON.decode(responseText);
      });
  }
When the response comes back, I decode the JSON and assign the results to the list of appointments. It does not get much simpler than that.

Of course, I am now left with no appointments in the application since my noSQL store is empty. To persist entries that I make, I need to update the add() method from last night to include an HTTP POST:
  void add() {
    var newAppt = fromText(newAppointmentText);
    appointments.add(newAppt);
    newAppointmentText = null;
    HttpRequest.
      request(
        '/appointments', 
        method: 'POST', 
        sendData: JSON.encode(newAppt)
      );
  }
And, just like that, I am able to add records that persist in the server after a page reload:



Pfft. That was pretty easy. I seriously doubt the service approach is going to improve on this much, but I will give it a go anyway.

Working from the top down, I start in the main() entry point for my application by adding a new “type” to my Angular module—the ServerCtrl:
main() {
  var module = new AngularModule()
    ..type(ServerCtrl)
    ..type(AppointmentCtrl);

  bootstrapAngular([module]);
}
(n.b. bootstrapAngular() will be changing to ngBootstrap() in the next release)

The order in which the “types” are added to the module does not seem to matter. What does matter is the declared type for the constructor parameters of the classes. For the ServerCtrl, I declare an Http instance variable to be passed to the constructor:
class ServerCtrl {
  Http _http;
  ServerCtrl(this._http);
  // ...
}
That alone seems to be sufficient to get Angular.dart to inject the correct thing, which is pretty cool. I may have to dig into the code to figure out how they do that. Regardless, once I have my Http instance set, I can use it. For example, I can create an init() method that can be called by the appointments controller to initialize the list of appointments that have been persisted on the server:
class ServerCtrl {
  Http _http;
  ServerCtrl(this._http);

  init(AppointmentCtrl cal) {
    _http(method: 'GET', url: '/appointments').
      then((HttpResponse res) {
        res.data.forEach((d) {
          cal.appointments.add(d);
        });
      });
  }
}
Calling the _http object like that is a little more verbose than the getString() method on HttpRequest, though I do get the automatic conversion of the JSON response in the data property. And, if life in AngularJS has taught me anything, this should prove to be more testable as well (but that's a question for tomorrow).

I am not quite done, because now I need to get the AppointmentCtrl instance to call this init() method in the server controller. This turns out to be pretty easy thanks to that declared type trick. In the AppointmentCtrl constructor, I declare that it takes one argument—an instance of ServerCtrl. With that, Angular.dart will inject the active instance of ServerCtrl into the constructor so that I can invoke the init() method:
class AppointmentCtrl {
  // ...
  AppointmentCtrl(ServerCtrl server) {
    server.init(this);
  }
  // ...
}
And that works! Starting from the main() entry point, Angular is able to create an instance of my server controller and inject it into the constructor of the appointment controller when it creates an instance of that class. The appointment controller then injects itself into the server's init() method to load the initial list of appointments.

That is a lot of injection and I am not completely convinced that it is an improvement from a code clarity point of view. But it is a definite improvement in separating concerns, which should lead to improved testing and maintainability. I will put that theory to the test tomorrow.


Day #910

Saturday, October 19, 2013

A Classical Controller in Angular.dart


Tonight, I continue my exploration of Angular.dart, the Dart port of the execllent AngularJS. I am starting to develop a feel for the differences between the original JavaScript and Dart versions. So far the differences have been something of an impedance mismatch, where the Dart version felt clunky compared with what I am used to in AngularJS (not that I've done a ton of AngularJS).

I think Angular.dart made a pretty solid first attempt to mimic the AngularJS controller structure. Ultimately, the difference between prototypical and classical inheritance was probably a bit too much. JavaScript's ability to access object literals with the dot operator whereas Dart maps can only use square brackets further weakens the attempt.

I don't think that I am revealing any big secrets here. The Angular.dart folks figured this out long before I started looking at the project. Indeed, they have already started on a new and improved approach that plays better to Dart's strengths. So tonight, I am going to convert my current AngularJS-like approach:
class AppointmentCtrl {
  AppointmentCtrl(Scope scope) {
    scope
      ..['appointments'] = [{'time': '08:00', 'title': 'Wake Up'}]
      ..['addAppointment'] = (){
            var newAppt = fromText(scope['appointmentText']);
            scope['appointments'].add(newAppt);
            scope['appointmentText'] = null;
          };
  }
  // ...
}
In the current approach, nearly all of the work is done in the constructor, which requires an injection “scope.” Even in AngularJS, I am a little uncomfortable with $scope, which seems to attract cruft (though the hierarchy built into Angular ensures this isn't too bad). Regardless, it just looks ugly doing all that work in a constructor.

It turns out that constructors are not needed at all. Instead, I can convert the list of appointments to an instance variable and the addAppointment function into a method, which I renamed as the more compact add():
class AppointmentCtrl {
  List appointments = [{'time': '08:00', 'title': 'Wake Up'}];
  String newAppointmentText;

  void add() {
    var newAppt = fromText(newAppointmentText);
    appointments.add(newAppt);
    newAppointmentText = null;
  }
  // ...
}
That is much clearer and reads like any other Dart class. I like it!

The other change that I need to make inside of the class is to expose a text value to mirror the ng-model element of the same name in the HTML. Here, I am using newAppointmentText:
class AppointmentCtrl {
  List appointments = [{'time': '08:00', 'title': 'Wake Up'}];
  String newAppointmentText;

  void add() {
    var newAppt = fromText(newAppointmentText);
    appointments.add(newAppt);
    newAppointmentText = null;
  }
  // ...
}
The add() method then uses this value to get input from the HTML counterpart, convert it into an appointment data structure, and add the result to the list of appointments. After accepting input, the add() completes its work by resetting the ng-model attribute back to null so that the user can add another appointment from the UI.

There is one other bit of work required in the backing controller class. I need to add a publishAs attribute to the @NgDirective above the class:
@NgDirective(
  selector: '[appt-controller]',
  publishAs: 'day'
)
class AppointmentCtrl {
  // ...
}
The selector attribute of the annotation tells Angular.dart how to identify the containing HTML element (e.g. <div appt-controller>). The publishAs attribute tells Angular.dart how it can refer to the controller instance. For every element with the declared selector, any child elements can now refer to the associated AppointmetnCtrl instance with the day identifier.

Thus, my HTML becomes:
    <div appt-controller>
      <ul class="unstyled">
        <li ng-repeat="appt in day.appointments">
          {{appt.time}} {{appt.title}}
        </li>
      </ul>
      <form onsubmit="return false;" class="form-inline">
        <input ng-model="day.newAppointmentText" type="text" size="30"
               placeholder="15:00 Learn Dart">
        <input ng-click="day.add()" class="btn-primary" type="submit" value="add">
      </form>
    </div>
Now the ng-repeat iterates over day.appointments, the ng-click invokes the day.add() method, and the ng-model refers to the day.newAppointmentText property. Thanks to the publishAs, each of these is exactly like accessing a property or invoking a method on an instance of AppointmentCtrl named day. There still a bit of magic at work here—the day instance is “just there” by virtue of appt-controller—but I do prefer an object being automatically created than a bunch of functions and variables being around. It seems more real, somehow.

Coming from a AngularJS background, I found the barrier to entry lowest with the previous approach that mimicked AngularJS controllers by injecting scope into the constructor. But I quickly found it off-putting and awkward. This class-based approach feels like home.


Day #909

Friday, October 18, 2013

Form Handling Angular.dart Controllers


Life is best on the bleeding edge. Sure the danger is plentiful, but where better to learn about what you're really made of than when death threatens you by the minute? I think I lost track of that metaphor quicker than normal. No matter, I do enjoy the bleeding edge as a great place to explore what's next and, more importantly, why things are changing the way they are.

So far, my exploration of Angular.dart, the Dart port of the awesome AngularJS framework, has not dissappointed. Before moving much further, I think I need to move beyond the bleeding edge (is that going to plaid?). The 0.2 version of Angular.dart has been useful, but things are changing quickly and I would like to be better prepared (and leave more useful posts behind). But first...

I have the first scraps of Angular Calendar in place: the day view can list existing appointments:



This works thanks to an Angular controller linked via the appt-controller attribute in the container <div>:
    <div appt-controller>
      <ul class="unstyled">
        <li ng-repeat="appt in appointments">
          {{appt.time}} {{appt.title}}
        </li>
      </ul>
      <form ng-submit="addAppointment" class="form-inline">
        <input type="text" ng-model="appointmentText" size="30"
               placeholder="15:00 Learn Dart">
        <input class="btn-primary" type="submit" value="add">
      </form>
    </div>
The ng-repeat directive works by reading the property from the controller. But the ng-submit does not work because it is not in Angular.dart yet. That is a bummer, but hope is not lost.

Even though ng-submit is not supported (yet), ng-click is supported. So I have to tell the form not to submit (using a yucky, old onsubmit) and then attaching behavior to the button via ng-click:
    <div appt-controller>
      <ul><!-- ... --></ul>
      <form onsubmit="return false;" class="form-inline">
        <input ng-model="appointmentText" type="text" size="30"
               placeholder="15:00 Learn Dart">
        <input ng-click="addAppointment()" class="btn-primary" type="submit" value="add">
      </form>
    </div>
Note that I am still using appointmentText as the ng-model.

The controller class backing this markup from yesterday is:
@NgDirective(
  selector: '[appt-controller]'
)
class AppointmentCtrl {
  AppointmentCtrl(Scope scope) {
    scope
      ..['appointments'] = [{'time': '08:00', 'title': 'Wake Up'}]
      ..['addAppointment'] = (){print('yo');};
  }
}
The selector in the @NgDirective annotation is what Angular.dart uses to connect the container element with the controller. The constructor for the controller is passed the scope, which it uses to assign the default list of appointments (a single “Wake Up” entry) and define the addAppointment function. And, with the ng-click directive properly in place, I am now seeing the “yo” from that function printed to the console when I click the “add” button:



To get this function to actually add appointments to the list, I write a small fromText() function that converts the appointmentText ng-model from text into a Map. That value can be added to the scope's list of appointments. Lastly, the model is cleared so that other entries can be added:
class AppointmentCtrl {
  AppointmentCtrl(Scope scope) {
    scope
      ..['appointments'] = [{'time': '08:00', 'title': 'Wake Up'}]
      ..['addAppointment'] = (){
            var newAppt = fromText(scope['appointmentText']);
            scope['appointments'].add(newAppt);
            scope['appointmentText'] = '';
          };
  }

  Map fromText(v) { /* ... */ }
}
And that does the trick. I can now add the entire list of appointments to my daily calendar:



Nice! That turned out to be a little easier than expected. For completeness, the fromText method, with some rudimentary support for defaults time and title, is:
  Map fromText(v) {
    var appt = {'time': '00:00', 'title': 'New Appointment'};

    var time_re = new RegExp(r"(\d\d:\d\d)\s+(.*)");
    if (time_re.hasMatch(v)) {
      appt['time'] = time_re.firstMatch(v)[1];
      appt['title'] = time_re.firstMatch(v)[2];
    }
    else {
      appt['title'] = v;
    }

    return appt;
  }
I find the handling of scope awkward when compared to the current AngularJS $scope. This is mostly a function of $scope being an object in JavaScript where it is a Map in Dart. While the Map is the appropriate structure, the bracket lookup looks ugly when compared with the dot notation for accessing the same values in JavaScript. The method cascade helps some, but not enough.

Happily, the Angular.dart folks are already on top of this. Both the recent 0.2 release and the master branch boast a newer, more classical class based approach. I will explore that tomorrow.

For now, I am pleased with the progress that I have made. Despite the lack of ng-submit, it is still possible to create the usual awesome Angular forms, and do so fairly easily. I am looking forward to exploring this stuff more—no matter how dangerous (or plaid) the code may prove.


Day #908