Starting with a naive MVC application my goal was to provide automated testing over headless graphics (e.g. using a standard test suite). How can we modify an application so that it can be used either by a real world user or by a robot?
I started with a concept (‘the user agent’). This is somewhat useful and helped clarify the application structure. However, it seems a better way to get this to work is may be to just write a couple of tests ‘assuming the underlying system’ is already compatible with our new requirements – in other words, design the solution outside-in.
Additionally, I’m looking into existing automations for iOS; I will shortly review a couple of attractive solutions.
The user agent
A so called user agent (I may be abusing the term) is an interface bridging the view and the controller. Why would it be useful to complicate our design in this way?
Initially, an MVC application works like this:
USER <=> {View} <=> {Controller} <=> { Model } (2)
With a testing automation, it may look like this:
{ Robot } <=> {View} <=> {Controller} <=> { Model }
Indeed certain automations work this way (great); what about testing over headless graphics?
- The view component must not be instantiated.
- We cannot instantiate the controller unless we hide/factor out view dependencies.
We could write tests that only exercise the model; not bad, maybe too far removed from our goal.
So the idea is to introduce an interface (‘user agent’) bridging the view and controller; then we could setup the application in either of two ways:
USER <=> View <=> { UA } <=> {Controller} <=> { Model }
{ Robot } <=> { UA } <=> {Controller} <=> { Model }
A quick refactoring
Clearly dependencies between the view and controller need to be factored out in order to realize the above. Initially the controller owns a reference to the view and may processes UI events.
One way to proceed:
- Migrate event handling code to the UI; inside event handling code we invoke controller methods.
- Provide interfaces (e.g. IUser or IUserInput, IUserOutput) that the controller uses to output to, or configure input from, the UI. The UI implements these interfaces.
- RootController
- ControllerA
- ControllerB
- <IUser> specifies methods invoked by the controller in order to display output or configure user input
- <IApplication> specifies methods normally invoked by the UI in order to operate the application. Invocations of IApplication may be recorded for later playback.
- A Robot class may define high level methods used to manually write tests.
To be continued…
My impression at this point is that I may have jumped in too quickly – starting to design based on a ‘good idea’ instead of formalizing use cases.
The UA idea is ‘kind of good’. But it took time to move from the idea to an understanding of what the UA might actually be.
Actually, the most productive step was not thinking about the UI but defining a new initializer for the session, like thus:
Session initWithContext:(id<SessionContext>)context
Where:
- Session represents a user session; roughly matches the model/controller.
- SessionContext is intended to capture anything that a user session may require to boot and run, with or without headless graphics.
- A live session context would provide a UI.
- A testing context would not provide a UI.
- Session can be instantiated in a headless graphics environment.
If a session can be defined in this way, we have done half of the work.
(tbc)

