Update: this post talks only about one form of event handling. Read this other post for a more detailed explanation on event oriented programming with JavaScript.
Back in the old days of desktop development (gtk, qt, swing, delphi, windows.forms…) it was very common to use events as the primary means of communication between artifacts. However, automated testing wasn’t very common at that point and there was a lack of tools for ui automation. With the web, we haven’t really used events in more than 10 years (asp.net tried to simulate them) but now with javascript we are back to the old techniques. There have been very few changes in software architecture and patterns in the last 20 years. We could have been writing web applications in the browser the way we do today, more than 10 years ago, if the big companies had agreed on standards and all that. It’s always about people, not about software 🙂
Anyway, for those friends of mine that are already practicing TDD but haven’t really used events, it is not clear how to test drive a collaboration between objects, using events.
Let’s see the simplest way to publish and subscribe to an event using javascript:
function Publisher(){
this.onWeirdData = function(){} // event
this.someMethod = function(someData){
if (someData.isWeird)
this.onWeirdData(); // fires the event
}
}
function Subscriber(publisher, errorMessageBox){
this.errorMessageBox = errorMessageBox;
var handleWeirdData = function(){ // private event handler
errorMessageBox.show("error: weird data");
}
publisher.onWeirdData = function(){ // subscribes to event
handleWeirdData();
}
}
This is not a proper implementation of the “Observer pattern” as only one object can
subscribe to the event. We basically replace the publisher event implementation which was
intentionally left blank.
In most cases, I don’t need the publisher to have a list of observers but just one, so this way of event firing/handling is OK for me.
Now imagine that we haven’t written the code above and we start with our test first:
I want my object to fire an event as weird data is received
it("fires event when data is weird", function(){
var fired = false;
publisher.onWeirdData = function(){ // spy on the event
fired = true;
};
publisher.someMethod({isWeirdData: true});
expect(fired).toBeTruthy();
});
I want my objects to interact through events
it("asks the error box to show when the publisher fires weird data event", function(){
spyOn(observer.errorMessageBox, "show");
publisher.onWeirdData();
expect(observer.errorMessageBox.show).toHaveBeenCalled();
});
Notice that I haven’t spied on the private method “handleWeirdData”. First because it is private, but even if it was public, I don’t want to expose that detail in the test. I just want to express the behavior that I expect from my objects.