[TFUI] The TFUI Principles

Phlip <[email protected]>
Newsgroups gmane.comp.programming.test-first-user-interfaces
Message-ID <[email protected]>
Gang:

I have returned to my perennial essay (after a sabatical applying
Broadband Feedback to a crunch-mode situation...).

	The TFUI Principles
GUIs and GUI testing provide a roadbump to those learning Test Driven
Development. Given a difficult combination of experience, tools, and
goals, folks meet with common problems and mistakes. This essay
addresses the problems and issues GUIs generate, and constructs a
strategy that ports to any GUI situation.

	What is Test Driven Development?
Briefly, write failing tests, write code to pass them, and then
refactor the code to improve its design. After the fewest possible
edits - say ten at the most - run all the tests, and predict their
results. This technique makes the odds of bugs and the odds of excess
refactoring very low.

	Why Test FIRST?
Why wash your hands before performing surgery? TDD is a good habit
that trades long hours debugging for short minutes writing new test
cases. Prevention is better than a cure. If tests unexpectedly fail,
passing tests should only be an Undo away.

	Why is TDD for GUIs naturally hard?
The target situation this: We should be able to write new test cases
that force /predictable/ changes in a GUI's appearance and response.
All other libraries submit to program control, making prediction easy.
But GUIs have a side that only users can see or touch.

We will address this problem by enabling test cases that temporarily
present GUIs, under test, for inspection and interaction. This, in
turn, allows us to grow extra test code - test fixtures - to increase
the odds that we can predict GUI changes.

	Why is TDD for GUIs artificially hard?
GUI Toolkits have steep learning curves. This inspires toolkit vendors
to compete to bundle suites of helper applications - wizards, form
painters, debuggers, etc. - with their GUIs. A good GUI tool can
flatten a GUI Toolkit's learning curve.

So the GUI Layer, within a large project, may suffer neglect from much
of a team's total experience. Because the backend of a project - the
Logic Layer - is mysterious, complex, and valuable, it often receives
more senior attention, and more careful development practices. When
leaders think that GUI development is "easy", they might not pay so
much attention to the growth of its design.

To put it bluntly, senior developers carefully write Logic Layers, and
associate developers paint and debug GUIs.

GUI Toolkit vendors reinforce this culture. Their marketeers claim,
"Anyone can write simple forms with our system!" The fun starts when a
team attempts to scale these simple forms up to complex and
well-balanced applications. While most of the Logic Layers in our
industry suffer from the bad effects of Waterfall, GUI development
labors in the salt mines of Code-and-Fix. Then GUI Toolkit vendors
reinforce the situation by providing elaborate and tempting wizards
and debuggers, to help you write code you don't understand, and then
to help you debug it until it works.

	How to avoid TDDing a GUI?
The first line of defense, in any Agile project, is to avoid fixing a
potential problem until it can be successfully forced to the surface.
Engineers try to cause the next problem they intend to solve. If they
can't cause the problem, they are finished.

For GUIs, this strategy leads to the idea that as much of a GUI's
logic as possible should be written in a separate module, decoupled
from the GUI. We will observe this decoupling by following a very
simple rule, with a name. The part of an application that is shaped
like the GUI, but is decoupled from it, is the "Representation Layer".
This term corresponds to terms in other papers such as "Logical User
Interface" or "Presentation Layer". We use our Representation Layer to
convert one format to another - the Logic Layer's format to the format
the user experiences. However, we can easily distinguish our
Representation Layer by applying a very simple and important rule:

	A Representation Layer may not use any identifier from the GUI Toolkit. 

If your GUI Toolkit provides, say, "Label", "Form", and "Pushbutton"
identifiers, your Representation Layer must not use any of them. This
technique exposes your Representation Layer to the full benefit of
logical design techniques, such as TDD, without the encumbrance of GUI
testing.

Anything the user can do to a GUI, a programmer can do, the same way,
to a Representation Layer. If the user can scroll a list box and
select a name, then a programmer could write statements that fetch a
list of names with a method, and could select the index of one name
with another method. The Logic Layer may or may not keep any names in
any list; the Representation Layer re-arranges data to satisfy user
needs.

Anything the GUI can do to a user, the Representation Layer must do to
a programmer. If selecting an item from ListBoxA repopulates ListBoxB,
then the Representation Layer provides a callback or an Observer
Pattern event to trigger this repopulation.

When the GUI Layer is very thin, it becomes humble. It only delegates
all responsibilities directly to the next layer.

	Why bother TDDing the GUI?
Why do we insist on doing the impossible? No matter how thin you think
your GUI Layer is, and how much logic you push out into the
Representation Layer, the simple fact is that bugs spontaneously
generate in code without tests. TDD will illustrate this effect by
pushing the defect rate down, allowing the testless parts of your
program to stick out like sore thumbs.

Many GUIs, including mine, will grow and live happy lives without
tests. We could easily follow a policy of "Ain't broke don't fix it."
We could decide not to write tests in for some of our modules, until
we need to. We could decide to "capture bugs with tests", and only
write tests after we detect bugs. So by attempting to follow the Agile
aphorism "You Aren't Gonna Need It", we might defer labor hoping to
permanently avoid it.

That strategy has one major flaw, called "activation energy". If a
module already has tests, new tests are very easy to write. The
activation energy is low. However, modules without tests make new ones
very hard to write. (Hence books on the subject, such as /Working
Effectively with Legacy Code/ by Mike Feathers.) The activation energy
is much higher.

When you receive a defect report, the energy required to "just fix it"
is usually lower than the energy required to retrofit tests, capture
the bug with test(s), and /then/ kill it. So each time you "just fix
it", and put off the labor, the next time gets easier. And more
likely.

Tests are the only aspect of Agile development that you /are/ going to need.

All new modules, including GUIs, must start with tests. That's the
only way to ensure that test rigs grow, with their modules, and remain
ready to assist in all advanced development.

	How to TDD a GUI?
Wizards, form painters, and debuggers are useful tools. We start TFUI
by making sure our lowly test cases can compete with them. We need to
spend more time among our test cases than among our vendor's tools'
GUIs, or our target application's GUI.

That requires giving our test cases a GUI of their own.

To get there efficiently, without spawning a new corporate division,
we will grow this GUI only by responding to real problems in our
project. This system works by giving names to solutions for those
problems, and ensuring the solutions reinforce each other.

If you use an off-the-shelf GUI test system, you can easily lead it
towards this system. However, even the most limited GUI Toolkits can
grow this system from their primitive components.

	One Test Button
To test, keep your hands on the keyboard or mouse, and tap one
function key or button. The button invokes a test rig to save all
changed files, run selected tests, and report the results back, as
fast as possible, and with no further interaction.

This principle generates all the other principles. They are all
reactions to common reasons we might need to perform any other
interaction at test time.

	Just Another Library
When you learn a GUI Toolkit by learning to use its form painter, you
may begin to think in terms of "GUI on the outside, source code on the
inside". To learn to take control of windows and forms /as objects/,
we set a very simple goal:

During a test run, permit the fewest possible obnoxious side effects.
Run as many test cases as possible without displaying windows.

That's just a goal; it's not a rule. Trying to achieve the goal
generates many useful side-effects. Most windows can react to display
commands without displaying their results on your desktop. When you
harness that effect, your tests will run quick and quiet. And that, in
turn, leads to less hesitation before hitting the One Test Button.

	Regulate the Event Queue
To take control of a window, under test, you must learn how its event
queue dispatches messages. Some messages you must throttle -
especially the paint messages. And some messages you must let thru,
such a test case's messages that simulate user input.

Control over those aspects provides enough tools for us to start
giving our test cases a GUI. Write a function called 'reveal()', and
temporarily call it from test cases.

	Temporary Visual Inspections
Turn on the Event Queue, during a test case, to visually inspect its
window populated by test data. The test run shall block until the
window closes, then subsequent tests shall run.

	Temporary Interactive Tests
While the GUI displays, use it to manually research GUI Toolkit behavior.

Those two practices provide our test case's GUI. The reveal() fixture
displays our testee window, populated with test data. That allows us
to spot-check our test case, and make. Then we comment out the
reveal() call, and ensure the test code works without visual
inspections.

And the ability to temporarily reveal() leads to a convenient platform
to record images of windows under test.

	Broadband Feedback
GUIs can record animations of activities during tests. Acceptance Test
Frameworks can command test cases to record a gallery of results.

The previous 6 Principles force the review of GUI appearance and
behavior into the tests, away from the wizards, form painters, and
finished application. They allow us to take control of the situation,
and prevent the need to excessively manually test.

Now we leverage those Principles to develop test fixtures (test-side
reusable methods) that enable predictable changes.

	Query Visual Appearance
GUI Toolkits provide the ability to query what they would look like if
you could see them. All such queries are simulations. Tests must learn
how to query that a GUI correctly obeyed the commands from its GUI
Layer code.
	o Tests on Controls may Get details which Production code Set.
	o Tests on Scripts parse the script, looking for details.
	o Tests on Paint() events use Mock Graphics objects to generate a Log String.

	Simulate User Input 
Test cases simulate events and check responses.
	o Loose User Simulations
		Call the same method as an event handler would have called.
	o Firm User Simulations
		Find the handler bound to an event and call it.
	o Strict User Simulations
		Push a raw input event into the event queue.

	Fault Navigation 
After a test run, the engineer can tell if a compile fails, or a test
fails, or the code faults, without pressing any other button besides
the One Testing Button. After a failure, the engineer optionally
automatically navigate to the failing line.

	Flow
During tests, the editor remains available, activated, and unblocked.

GUI development depends on focussing on those problems early, and
finding solutions for the intersection of your GUI Toolkit and your
target application.

Many of those ideas seem obvious. However, for every GUI project that
spontaneously solved them, there is another that missed a few. The
idea that your editor should remain unblocked during a test run might
seem impossible to programmers familiar with, say, Visual Basic 6.

Put together, the strategy seems to resemble a paradox. To avoid
manually reviewing GUIs, you must write a reveal() method that
manually, temporarily reveals a GUI, under test. This is not a
paradox; it is taking control of the situation. Wizards, form
painters, and debuggers no longer control us. We control when and how
to manually review our GUIs. When test cases temporarily present GUIs
in their exact tested state, differences between those GUIs and the
test assertions' opinions become easy to fix.

----

The remaining verbiage consists of simple but very powerful projects
grown from scratch using these principles.

-- 
  Phlip


To unsubscribe, email:
TestFirstUserInterfaces-unsubscribe-hHKSG33TihhbjbujkaE4pw@public.gmane.org
 
Yahoo! Groups Links

<*> To visit your group on the web, go to:
    http://groups.yahoo.com/group/TestFirstUserInterfaces/

<*> To unsubscribe from this group, send an email to:
    TestFirstUserInterfaces-unsubscribe-hHKSG33TihhbjbujkaE4pw@public.gmane.org

<*> Your use of Yahoo! Groups is subject to:
    http://docs.yahoo.com/info/terms/
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.