Test First User Interfaces
Revision r1.2 - 13 May 2004 - 21:50 GMT - PhlIpA mailing list to discuss testing hard situations:
http://groups.yahoo.com/group/TestFirstUserInterfaces/
Tests on GUIs should not display any windows. But
programmers writing such tests should display them
early and often.
Here's an example of TFUI in Ruby, using the TkCanvas?:
http://www.rubygarden.org/ruby?SvgCanvas
The source is not on that page, but it uses a function
called maybeMainloop(), which should be called
reveal().
Here's an example using Gtk+, and a function called
reveal():
http://gnomesupport.org/wiki/index.php/TestDrivenDevelopment
(A lot of you are wondering "why not Java"? Because
Ruby can look like Java, with typecasts added and
block closures taken out.)
Here's the "Spigot" technique in Qt (C++ for Linux, KDE, and embeddings):
http://wiki.linuxquestions.org/wiki/Qt/TestDrivenDevelopment
Here's a near-TFUI sketch in Java, using HttpUnit?:
http://www.c2.com/cgi/wiki?HttpUnitTutorial
It uses reveal() to display today's Dilbert. (I
recently introduced a Romanian programmer to the
existence of Dilbert, leaving me feeling quite
fulfilled and glowing...)
A lot of you are wondering "what's with the reveal()"?
This sample says it very succintly:
http://www.rubygarden.org/ruby?WebUnit
It uses Ruby and WebUnit?, which designs to compete
with HttpUnit?. (I promise - if I wrote a project in
Java I'd use HttpUnit?.)
A TFUI test case looks like this, in a hypothetical
(but vaguely Ruby-like) language:
def test_Window()
win = CreateMyAppWindow()
assert_equal("My App", win.GetTitle())
# new code goes here
# reveal(win)
end
The call to reveal(win) is commented out.
(Those who miss Java can add a lot of 'private' and {}
and redundant type declarations all around it. Don't
get me wrong - I like Java. All its extra stuff make
it a great language to illustrate refactoring with.)
If you de-comment reveal() and run the test batch,
this window pops up, and control flow blocks until you
close it.
If you dislike the window's appearance, add assertions
before the reveal() call, and then program until you
can see the window again. If you still dislike it,
repeat.
If you approve of the window, comment out the reveal()
line, and integrate. (Don't integrate with the
reveal() line turned on!)
If you need to test how the window behaves when
clicked-on, turn reveal() on, then click on the
window. (If you reveal() from the bottom of a test
that propels the window into an interesting state, you
will see it fully populated with data, so you don't
need to type that all in.)
After clicking on the window, comment the reveal()
out, and then write more test code that does the same
thing to your code as clicking on the window did. You
might call your own callback method directly (yes,
even if it is private). Or you might research, a
little, to learn how to invoke your GUI Toolkit's
event system for the clicked-on control.
Calling 'reveal()' to write automated tests that
should not reveal their windows might seem like
cheating.
It is.
Traditional GUI development couples to form painters
(and debuggers) that couple programmer behavior to
window appearances. Your cycle is
paint->code->debug->paint. This cycle does what
reveal() does for us, but at the wrong times. You
paint when the controls are not populated, and you
debug when they are manually populated.
Writing a test to call 'reveal()' takes control of
this situation. We efficiently display the window
exactly when we need need it. Form painters,
debuggers, and even Web servers no longer control us.
When you develop a GUI, invent a reveal() function
that works on any of your windows. Your teardown
should destroy windows, whether displayed or not, so
they don't bunch up in memory. You also should not
have to "think" too much before typing reveal(win) and
seeing a window. If your app needs temporary data or
folders or whatever, or your windows are special and
need a separate hardware screen, then work on reveal()
to take care of all of this.
Put another way, if you are thinking about your window
- and not about reveal() - then you should not have to
think about anything to do with reveal() except typing
it and running all the tests.
Some examples for various libraries:
-- PhlIp - 12 May 2004 |
|
|