In my last entry I took a look at unit testing the classes of a simple Presentation Model application. In this entry I’m going to turn my attention to a functionally-identical Supervising Presenter application. If you cast your mind back to my original investigation of these patterns you’ll remember that the main difference between Presentation Model and Supervising Presenter is that the former extracts both state and logic from the view whereas the latter only extracts logic. So in order to receive user events and manipulate view state in response to these events, supervising presenters maintain a reference to their corresponding view.
The first challenge when unit testing supervising presenters is the dependency they have on the view. Since I am trying to follow a strict unit testing strategy, I want my supervising presenters to be tested independently of other classes, including view classes. So I need to find some way to isolate my presenters from their corresponding views. Besides this unit-testing purism, there are other reasons for isolating my tests from the view:
- Views are generally relatively slow to instantiate and require a disproportionate amount of resources. Having to create views in order to test classes that depend on them may slow down the running of my test suites.
- Animation, deferred instantiaion and modal behaviour could make unit testing challenging.
There are probably many ways to decouple presenters from views, but I’ve decided to follow Martin Fowler’s suggestion of creating a "Gateway" between the view and the presenter. Each gateway will have two responsibilities:
- Act as a facade for a view. Presenters will access and manipulate the view via the facade.
- Observe the view for user events and invoke operations on the presenter.
Each view-dependent gateway will implement an interface to allow it to be substituted with a view-independent stub class for use during testing. This UML diagram illustrates the impact of the gateway approach on the original design.
The second challenge is the collaborative relationship between supervising presenters. In order to isolate one supervising presenter from another I’m going to use the factory method approach described in my previous entry.
Unit testing AlbumFormPresenter
The presenter for AlbumForm does not collaborate with any other presenter objects, but I do need to create a gateway class in order to isolate it from the view. I had to make a few changes to the architecture in order to slot in my gateway, and the end result is a gateway object between the view and the presenter called AlbumFormGateway. This gateway class implements the interface IAlbumFormGateway, so that it can be replaced with a test double called StubAlbumFormGateway. The test double does not require the real view, and the AlbumFormPresenter does not differentiate between the real gateway and the stub gateway because they both implement the same interface. So now I can unit test my AlbumFormPresenter class without needing an instance of AlbumForm.
Unit testing AlbumBrowserPresenter
I also need to create a gateway object for AlbumBrowserPresenter to decouple it from the corresponding view class. This is achieved in precisely the same way as for AlbumFormPresenter. So I’ve added an AlbumPresenterGateway, IAlbumPresenterGateway and StubAlbumPresenterGateway.
In addition to creating the gateway class, I also need to isolate the functionality of AlbumBrowserPresenter from AlbumFormPresenter. If you recall from my previous entry, a similar relationship existed between the AlbumBrowser and AlbumForm presentation models. So I’m applying the same factory method approach approach to allow the real AlbumFormPresenter to be replaced by a stub. The result is a new interface IAlbumFormPresenter, and a new stub class StubAlbumFormPresenter.
A further complexity for AlbumBrowserPresenter is the dependency it has on the PopUpManager. Rather than stub the PopUpManager class I have placed it on the ‘view-side’ of the gateway; so the presenter displays the pop-up by calling a method on the gateway.
Unit testing Album
Unlike the Presentation Model applicaton, the Album VO in the Supervising Presenter example doesn’t have any methods, so it doesn’t need to be tested. Or does it?
In order for my application to work the Album VO class does need to do one very important job: It needs to support binding. Should I write unit tests for bindability? Well strictly speaking I should, and perhaps I could use Tom’s EventfulTestCase for this. But I think I’ll look more closely at this another day.
Unit testing AlbumDelegate
As with the Presentation Model example, I’m not testing this class.
The supervising presenter’s view dependency does make unit testing more challenging. The gateway approach appears to be a robust strategy for isolating supervising presenters from views, but it comes at a price. The gateway layer adds extra complexity to the application, and the gateways themselves cannot be unit-tested because they still have dependencies on views.
After going to the trouble of creating gateways I’m left wondering whether it would be simpler to create test doubles for my actual view classes and dispense with gateways altogether. To do this I would have to extract an interface for each view and then create standalone test doubles that contain identical framework controls. For this to work I would also need to find some way to create fake framework events for my presenters to respond to. I’m not going to attempt this because I now want to turn my attention to unit testing autonomous views.