Archive for April 2007

The Testable UI

I just read Jeremy Miller’s ideas on creating a testable UI, so I thought I’d respond with my own. Mine has the distinction of being used successfully in a working product. That said, we are still aggressively developing the product, so it has not been proven to everyone’s satisfaction at this point.

Let me restate the problem first. It is very hard to test the logic behind a Windows application. Some techniques exist, but they all suck in their own special way. My technique also sucks in some ways, but it addresses the following design goals with varying degrees of success:

  • must be able to test the interactions between controls on a form (one value changes, then another must update or become enabled etc.)
  • must be able to test the processes that involve multiple forms
  • there should be zero user-code behind forms. Thus, the forms can be worked on independently of the business logic, and all business logic can be tested without instantiating forms. Business logic can also be reused by imports into the system
  • it should be easy for designers to build a form.
  • component responsibilities should be well-defined so that it is easy for programmers to know where business logic should be coded (and thus to find it later when they need to change it)
  • it should be easy to code, test and understand a previously coded sequence of actions (no confusing event-driven processes).
  • it should handle the OO conflict of business logic that fits badly within traditional entity objects, mostly because they need to behave differently in different business processes.

Here are some of the main components:

The UI Interface is an interface definitions containing a method for each dialog that the user may see. It is defined at the business layer, but implemented in the GUI. This allows the business logic to be tested with a mock GUI.

The Context is a holder of state data containing everything in the user’s view. Everything you could want is in the context. Having a context keeps APIs simpler, and prevents the Wormhole Anti-pattern. It is also one of the main weaknesses of testing the system, because it is not obvious what the context needs to contain in a particular scenario.

The RichScreen is a model of a real screen . It is a class defined at the business layer, but with a rich event-driven interface to be able to handle changes to fields. It is bound directly to one or more forms (the view), and can be directly persisted. Like Windows Forms, RichScreens have a lot of generated or inherited plumbing code. An instance of a rich screen may be thought of as a Business Entity.

The Commands are executable actions. They are sequential controllers (my own term) – this means that they strongly resist using events, thus ensuring readability. They have a single “Execute” method, and are often constructed with a reference to the context. Commands often make use of the UI interface. They are usually constructed by ExecutableMenus, which are rendered as menus or buttons in the user interface.

To bring it all together, a simple scenario:

  1. User chooses an “Add Order” menu item
  2. The ExecutableMenu attached to the real menu creates an Add Order command with a simple context. The Add Order command is executed.
  3. The Add Order command creates an empty order, initializes the order date to today, creates a RichScreen instance, and instructs the UI implementation to display the order.
  4. The UI implementation displays the order form. Data binding to the RichScreen is automatic.
  5. User enters some data and presses “Confirm Order” button.
  6. The context validate method is called, and uses metadata defined in the RichScreen to validate the data. Problems are discovered, and automatically highlighted on the form. The UI implementation is told to display the validation message, which it does with a message-box.
  7. The user corrects the data and re-presses “Confirm Order”.
  8. This time the validate completes and the context Save method is called. The order is persisted to the database
  9. The Add Order command continues, and sends a message to the order-system queue.