Unit Testing User Interfaces – Autonomous View

So far we’ve looked at possible unit test strategies for presentation models and supervising presenters. Now I want to turn my attention to the Autonomous View pattern. By leaving state and logic in the view, the Autonomous View pattern is arguably one of the simplest presentation patterns to understand and implement. But is it so easy to unit test?

Challenges

When attempting to unit test views directly, there are numerous challenges to overcome.

Challenge 1 – Isolating the views: Strictly speaking, to unit test an autonomous view class we should be creating stub or mock objects for all of the classes it collaborates with. But a typical autonomous view in Flex could collaborate with numerous Flex framework classes. In my example, the AlbumForm collaborates with three TextInputs, one Checkbox, and two Buttons; not to mention container classes like Form and FormItem. Stubbing all of these is no trivial task and not something I’m going to attempt. So strict unit testing of an autonomous view application is out.

A more sensible approach would be to treat each distinct autonomous view class as a "unit" for testing purposes, and assume that the Flex controls it contains are part of that unit. So I’ll create a unit test suite for AlbumForm and AlbumBrowser since these are distinct autonomous views, and when testing AlbumBrowser I’ll create a stub for AlbumForm.

Challenge 2 – Creating the view: When testing presentation models and supervising presenters I instantiated the "class under test" in my test suite’s setup method. But if I do the same for autonomous views, when I try to access a child of my view instance I get a null pointer exception. This is because my test code is being executed before the view’s CreationComplete event has fired. So to ensure that the CreationComplete event has been dispatched before my test runs, I have added an asynchronous setup method. This method instantiates the autonomous view, adds it to the display list and then uses the FlexUnit "addAsync" method to trigger the unit test code in response to the view’s CreationComplete method.

Challenge 3 – Simulating gestures: A further problem with unit testing my views is that much of their behaviour is driven directly by user activity. For example the change event of the TextInput class can only occur in response to user input. So I have the following options:

Fake the event by calling dispatchEvent on the control and passing it a suitable Event instance that I’ve created and populated myself..

The first of these appears to be the easiest, so that’s the option I’m going with.

Challenge 4 – Stubbing the Alert class: The AlbumBrowser class displays an alert box when the user navigates between albums without saving their changes. I want to test this behaviour, but I don’t want to see pop-ups appearing during a test run. So I’ve extracted the pop-up behaviour into a separate class (called YesNoDialogue), and added a stub class for use during test runs.

Challenge 5 – Stubbing child views: I’ve already said I’ll create a stub for AlbumForm in order to test AlbumBrowser in isolation of AlbumForm’s logic. I’ve done something similar in my previous entries for Presentation Model and Supervising Presenter. In those examples I used a factory method to allow real classes to be substituted with stubs. This works great in ActionScript, but is there a way to swap a real view with a stub when the real view is declared in MXML? Well I gave myself a full 15 minutes to think of one, but nothing came to mind. So in order to stub my AlbumForm, I’m forced to abandon MXML and create my child view in ActionScript. This is a significant compromise.

Example Application

With these challenges overcome or worked around, I could successfully unit test the Autonomous View demo. The unit tested version of the application is available here; right-click to view the source. The test runner for the unit tests is available here.

Thoughts

This isn’t so much a tutorial on how to unit test an autonomous view application as an example of why you should avoid trying to. Although many of the challenges can be resolved or worked-around, one in particular presents a real problem. The autonomous view pattern is attractive because of its simplicity; and a large part of that comes from the convenience of MXML for declarative layout. By abandoning MXML in order to unit test, I am undermining the simplicity of the pattern.

Further work

There may be clever ways to stub views in MXML and I admit I didn’t spend much time looking at how to achieve this. If you’ve found an elegant approach for testing this pattern without abandoning MXML I’d be very interested to hear about it, but I don’t intend to investigate the topic any further myself.