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:24I 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
No comments:
Post a Comment