Test driving an event driven design

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:

  2. function Publisher(){
  3. this.onWeirdData = function(){} // event
  5. this.someMethod = function(someData){
  6. if (someData.isWeird)
  7. this.onWeirdData(); // fires the event
  8. }
  9. }
  11. function Subscriber(publisher, errorMessageBox){
  12. this.errorMessageBox = errorMessageBox;
  14. var handleWeirdData = function(){ // private event handler
  15. errorMessageBox.show("error: weird data");
  16. }
  18. publisher.onWeirdData = function(){ // subscribes to event
  19. handleWeirdData();
  20. }
  21. }

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

  2. it("fires event when data is weird", function(){
  3. var fired = false;
  5. publisher.onWeirdData = function(){ // spy on the event
  6. fired = true;
  7. };
  9. publisher.someMethod({isWeirdData: true});
  11. expect(fired).toBeTruthy();
  12. });

I want my objects to interact through events

  2. it("asks the error box to show when the publisher fires weird data event", function(){
  3. spyOn(observer.errorMessageBox, "show");
  5. publisher.onWeirdData();
  7. expect(observer.errorMessageBox.show).toHaveBeenCalled();
  8. });

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.

  • Guillermo

    Back in the old days of desktop development (gtk, qt, swing, delphi, windows.forms…) <— Hey! i'm still doing a lot of WinForm development!!
    Anyway, thanks a lot!!! i'm starting to do TDD and that (Test Drive with objects colaborating with events) is one of those things that you don't see until you hit it.

  • Guillermo

    Me acabo de dar cuenta que posteaste en ingles y que yo respondí en Ingles 🙂 tengo que retomar las pastillas mañaneras…

  • Pingback: Event oriented programming with JavaScript « El blog de Carlos Ble()