« So You Want To Know What You're Bound To? | Main | Cursor you Microsoft SQL Server! »

Testable User Interfaces by Layering

User interfaces are notoriously difficult to test.  In recent architecture work, I've noticed that today's development culture, especially Test Driven Development (TDD) advocates, will significantly alter their frameworks and methodologies to facilitate a higher degree of testability.  For example, a primary reason cited for the added time and complexity of implementing the Model View Presenter (MVP) pattern is the purported testability benefits.  The pattern's advocates assert that separating a maximal amount of user interface logic into a non user-interface encapsulation, minimizes the amount of logic tied to the more difficult to test user interface.  Considering the TDD stance, where tests are written before the code, such layering would seem particularly imperative.

Let's step back a moment, however, and ask why user interfaces are difficult to test.  One reason might be that user interfaces are nearly the last thing to mature in an application, making them subject to the most variables.  Another reason, perhaps, is that GUI frameworks are often not designed with enough reflective APIs to allow programmatic automation of testing practical.  Probably the primary reason, however, is merely complexity.  Unlike most programmatic APIs, even a simple user interface typically consists of myriad potential action sequences, modalities, and interdependencies.

I'll get to my point.  Though user interactions may be complex, standard software engineering practices should be applied to mitigate the complexity.  Specifically, abstraction, layering, encapsulation and such.  This may seem obvious, but it is the case that standard practices involve building user interfaces at a relatively low levels of abstraction.  There are several conceptual levels of abstraction between forms, controls, and GUI logic, and the desired logical outcomes of data manipulation and analysis.  For instance, there is a layer that exists at the level of "here are a set of attributes that are to be edited by the user", which is typically stated at a much lower level ("here are some controls which are bound to...").  Among other advantages, making more layers explicit allows for each layer to be tested and verified independently.

These ideas may all seem theoretical, but are in fact based on my experience with Dataphor.  Because more of these conceptual layers were explicit, I found that user interface testing was almost never an issue.  Defects in the user interface were almost entirely either: a) in one of the abstraction layers (e.g. controls, or derivation engine); or b) manifestations of logical errors.  In the case of a, the defect could be resolved in the system and would fix all similar scenarios.  These instances were relatively rare, however, because it is practical to build automated tests for limited scope layers and the reuse of lower level layers naturally flushes out defects.  In the case of b, the exposure of logic errors is a desirable side-effect of declarative systems.  Due to these facets of Dataphor, I was able to build (generate mostly) literally hundreds of almost defect-free forms in a matter of a few weeks.

To conclude, rather than let the testing tail wag the architecture dog, it would seem wiser to better identify and automate the various conceptual abstraction layers and use the deductive method to maximize quality.  Architects should identify and automate more of what is common between their user interfaces, and concentrate on testing that automation.  With that, it won’t be necessary to test things like tab order, and accelerator key conflicts, because such things will have been automated.

TrackBack

TrackBack URL for this entry:
http://databaseconsultinggroup.com/blog-mt/mt-tb.fcgi/5