Unit Testing User Interfaces – Presentation Model

As mentioned in the introduction, I’m going to attempt to unit test my presentation model example application first. Of all the presentation patterns I’ve looked at, I’m expecting the presentation model to be the easiest to unit test.

Challenges

By extracting view state and logic from the view, presentation model classes do not have any dependencies on view classes. They do sometimes have dependencies on each other, so test doubles may be required to isolate the functionality in one presentation model from functionality in another.

Unit testing AlbumForm

The AlbumForm class is quite straightforward to unit test as it only collaborates with the Album value object. I’ve decided not to create a test double for the Album VO because of its simplicity. It only has too methods, and I suspect they will be difficult to simulate without duplicating their behaviour.

In the unit test for AlbumForm I have created tests for all of the public methods of the AlbumForm class. Many of the public methods require more than one test in order to exercise every path of execution.

Unit testing AlbumBrowser

The AlbumBrowser class is a little harder to unit test because it collaborates with the AlbumForm class. In order to isolate my AlbumBrowser unit tests from the functionality of AlbumForm, I’ve decided to create a test double for the AlbumForm class. To do this I’ve made the following changes:

  • Extracted an interface from AlbumForm – In order to create a test double for the AlbumForm, I’ve created an IAlbumForm interface which the AlbumForm now implements. The test double ‘StubAlbumForm’ also implementes IAlbumForm allowing it to be used as a substitute for AlbumForm.
  • Modified AlbumBrowser to work with any IAlbumForm implementation – Now there’s more than one way to do this, and I’ve used a factory method. The AlbumBrowser is modified so that it creates its instance of AlbumForm in a factory method (that’s just an instance method that is reponsible for constructing the AlbumForm). The factory method is marked protected so that it can be overriden in a subclass. In my AlbumBrowser unit test I’ve created a private subclass of AlbumBrowser that overrides the factory method to return an instance of StubAlbumForm*. My unit test for AlbumBrowser tests an instance of the private sub-class rather than AlbumBrowser itself. This allows me to test AlbumBrowser functionality in isolation of the AlbumForm class.
  • Added a property to StubAlbumForm to allow me to control the flow of tests – The AlbumBrowser takes different actions depending on the result from the ‘changesToSave’ method on the AlbumForm class. In order to test these different paths I’ve added a ‘pendingChanges’ property to the StubAlbumForm that allows me to specify the return value of the ‘changesToSave’ method.

I’ve also captured these modifications in the form of a UML diagram, so take a look if none of the above makes any sense.

Together, these changes allow me to test AlbumBrowser in isolation of the AlbumForm class. Doing all of this may be considered overkill for such a small application, but it becomes important for larger applications with deeper hierarchies of presentation model instances and more complex collaborations.

Unit testing Album

The Album VO doesn’t collaborate with any other objects, so testing it is very easy. The unit test for the Album class covers the two public methods: clone() and equals().

Unit testing AlbumDelegate

Since this class is a fake delegate, I’m not going to test it. In real applications it will be necessary to create test doubles for delegates.

Example Application

The unit tested version of the presentation model demo application is available here; right-click to view the source. The test runner for the unit tests is available here.

Thoughts

The presentation model’s independence from the view simplifies the unit testing of application-specific user interface logic. The main challenge is isolating the functionality of one presentation model from that of another. I’ve chosen to use a factory method approach in my application, but other creational patterns could also be applied, as could some form of dependency injection.

Further work

The presentation model classes in this application are still not very well encapsulated. Many of the properties the view binds to are public properties; this simplifies binding, but undermines encapsulation. I’d like to tighten up the encapsulation on these classes, but that’s a task for another day.

*A similar approach in Java would be to use Anonymous Inner Classes inside tests or fixtures to create test-specific sub-classes (here’s an example from IBM). EcmaScript and consequently ActionScript do not support this language feature, so I’ve used a private class instead.

2 Responses to Unit Testing User Interfaces – Presentation Model

  1. Leftie says:

    I have a question on how to structure test classes when you have a bit more of an advanced application.
    Say if you have a Flex Library project which contains most of your application, because you want it to be the base for one Flex Project and one Flex Air Project. If you want to test the classes in the Library, how would you do that?
    I can not run an application from the Library project. So what I looked at was having the runner in the Flex Project, however that made me have to include the test classes in the compiled SWC. Which is no good at all.

    So currently my best guess is to have a seperate project with all my test classes and the test runner and have it depend on the Library project. I don’t like this as it makes the tests and the code reside in two different projects, but I guess this is the only solution?
    If you have any recommended ways of doing that I would love to know.

  2. Hey Paul,

    i usually don’t start with a Library Project, but with a straight forward Flex Application. First off all i design the Domain model and simple views. I separate usually the model from the view simple by namespaces: fs.namespace.model or fs.namespace.view. For running the unit tests i use ANT to invoke compile the tests.

    When i now need to extract the model
    – project got too big, performance issues with the ide
    – model needs to accessible in other projects as well
    i simply put the model in a new project.

    With ANT you can copy the *.swc into the libs folder of the project or the work gets done via version control.

    Well… what i tried to point at: ANT might be the better way to work to avoid project dependencies which can lead to problems when working on several projects, in a network and so on…

    Mavon might be an option too since the Flex support grows.

    Best regards from Germany.