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: anyThen, 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










