Test-Driven Development in GUIs

J

Joe Van Dyk

Hi,

Anyone have any tips for writing GUIs in Ruby (using Tk, for example)
using a TDD approach?

A google search gave me
http://approximity.com/ruby/rubytk.html#testfirst, which claims that
some guy is writing a book on TDD for GUIs, using Ruby/Tk as preferred
language/toolkit. That section links to
http://www.c2.com/cgi/wiki?TestFirstUserInterfaces, while informative,
doesn't contain any information that I can see on a book.

Say, for the purposes of this discussion, say that you had a small Tk
application that monitored the status of ten websites. If the website
was up and was active and performing well, the GUI would display a
"Working!" message and perhaps showed data about the site (latency,
bandwidth, whatever). The GUI could also display graphs of
uptime/downtime, view history reports, and moderately complex GUI
stuff like that.

How would you go about testing that GUI application? There's a strong
chance that if I get some good ideas from my head and from this
discussion, that I'd write that application and also write a HOWTO on
TDD with GUIs.

Thanks,
Joe
 
D

Devin Mullins

TDDing UI is hard, because it's a side-effect, rather than a return
value or a parameter. I don't know the half of it. Nor, for that matter,
know how to use Tk. I present to you a "Hello world" example.

require 'test/unit'

class Greeter
def self.greet
puts 'Hello, world!'
end
end

class MockGreeter < Greeter
class << self
attr_reader :eek:ut
def puts(*args)
@out = @out ? [] : args
end
end
end

class TestGreeter < Test::Unit::TestCase
def testGreet
MockGreeter.greet
assert_equal ['Hello, world!'], MockGreeter.out
end
end

Subclass and override puts. Dependency injection doesn't help here. You
can have greet take a Module as a parameter, and call module.puts, but
you still need to test the container, and make sure it's passing Kernel
to the greet method.

Sorry, that's the short version. I gotta go.

Devin
 
J

Joe Van Dyk

Hi,
=20
Anyone have any tips for writing GUIs in Ruby (using Tk, for example)
using a TDD approach?
=20
A google search gave me
http://approximity.com/ruby/rubytk.html#testfirst, which claims that
some guy is writing a book on TDD for GUIs, using Ruby/Tk as preferred
language/toolkit. That section links to
http://www.c2.com/cgi/wiki?TestFirstUserInterfaces, while informative,
doesn't contain any information that I can see on a book.
=20
Say, for the purposes of this discussion, say that you had a small Tk
application that monitored the status of ten websites. If the website
was up and was active and performing well, the GUI would display a
"Working!" message and perhaps showed data about the site (latency,
bandwidth, whatever). The GUI could also display graphs of
uptime/downtime, view history reports, and moderately complex GUI
stuff like that.
=20
How would you go about testing that GUI application? There's a strong
chance that if I get some good ideas from my head and from this
discussion, that I'd write that application and also write a HOWTO on
TDD with GUIs.
=20
Thanks,
Joe
=20

Here's a very simple example of a quick UI app that's unit tested.=20
Comments appreciated.

If you run the application like 'ruby file.rb test', the tests will
get run. If you run it like 'ruby file.rb', the gui will be
displayed. In a real application, the tests would be in their own
file, of course.

As I do more research, I'll post more examples.



require 'tk'
require 'test/unit'

class App
attr_accessor :sum, :inc_button, :display_button, :power_2_button

def initialize
@sum =3D 0

root =3D TkRoot.new :title =3D> 'my app', :geometry =3D> '300x300'

@inc_button =3D TkButton.new(root,=20
:text =3D> "Increment!",
:command =3D> proc { inc }
)
@inc_button.pack :expand =3D> true=20

@power_2_button =3D TkButton.new(root,
:text =3D> "Power of 2",
:command =3D> proc { power2 }
)
@power_2_button.pack :expand =3D> true
end

def inc
@sum +=3D 1
display
end

def power2
@sum *=3D @sum
display
end

def display
puts @sum
end
end


class TestApp < Test::Unit::TestCase
def testInc
app =3D App.new

assert_equal 0, app.sum

app.inc_button.invoke
assert_equal 1, app.sum

app.inc_button.invoke
assert_equal 2, app.sum
end

def testPower2
app =3D App.new

assert_equal 0, app.sum
=20
app.power_2_button.invoke
assert_equal 0, app.sum

app.inc_button.invoke
assert_equal 1, app.sum

app.power_2_button.invoke
assert_equal 1, app.sum

app.inc_button.invoke
assert_equal 2, app.sum

app.power_2_button.invoke
app.power_2_button.invoke
assert_equal 16, app.sum
end
end

if ARGV[0] !=3D 'test'
App.new
Tk.mainloop
exit
end
 
J

Joe Van Dyk

=20
Here's a very simple example of a quick UI app that's unit tested.
Comments appreciated.
=20
If you run the application like 'ruby file.rb test', the tests will
get run. If you run it like 'ruby file.rb', the gui will be
displayed. In a real application, the tests would be in their own
file, of course.
=20
As I do more research, I'll post more examples.

Here's another iteration, this time the result is being displayed to a
TkLabel. The tests check to see if the label has the correct value.

So, when testing GUIs, is it good practice to have all the (tested)
widgets be available through attr_accessor (or attr_readers)? How
else can you test them?

require 'tk'
require 'test/unit'

class App
attr_accessor :sum, :inc_button, :display_button, :power_2_button,=20
:eek:utput_label

def initialize
@sum =3D TkVariable.new
@sum.value =3D 0

root =3D TkRoot.new :title =3D> 'my app'=20

@inc_button =3D TkButton.new root
@inc_button.text =3D "Increment"
@inc_button.command =3D proc { inc }
@inc_button.pack :expand =3D> true

@power_2_button =3D TkButton.new(root)
@power_2_button.text =3D "Power of 2"
@power_2_button.command =3D proc { power2 }
@power_2_button.pack :expand =3D> true

description_label =3D TkLabel.new
description_label.text =3D "Result:"
description_label.pack :expand =3D> true
@output_label =3D TkLabel.new
@output_label.textvariable =3D @sum
@output_label.pack :expand =3D> true
end

def inc
@sum.value =3D @sum.value.to_i + 1
end

def power2
@sum.value =3D @sum.value.to_i * @sum.value.to_i
end
end


class TestApp < Test::Unit::TestCase
def result(app)
app.output_label.text.to_i
end

def testInc
app =3D App.new

assert_equal 0, result(app)

app.inc_button.invoke
assert_equal 1, result(app)

app.inc_button.invoke
assert_equal 2, result(app)
end

def testPower2
app =3D App.new

assert_equal 0, result(app)

app.power_2_button.invoke
assert_equal 0, result(app)

app.inc_button.invoke
assert_equal 1, result(app)

app.power_2_button.invoke
assert_equal 1, result(app)

app.inc_button.invoke
assert_equal 2, result(app)

app.power_2_button.invoke
app.power_2_button.invoke
assert_equal 16, result(app)
end
end

if ARGV[0] !=3D 'test'
App.new
Tk.mainloop
exit
end
 
J

Joe Van Dyk

Tk question: How can I tell, via code, if a button is actually being
displayed on the screen?

Say buttons are being created based on data from a configuration file.
If I have a method that reads the config file and generates buttons,
how can I make sure those buttons are actually going to be displayed
on the screen?

I just ran into this problem when I forgot to 'pack' some buttons,
where 'packing' in Tk makes them visible/active on the screen. But I
don't know how to test for that.
 
D

Devin Mullins

Joe said:
Tk question: How can I tell, via code, if a button is actually being
displayed on the screen?
Look at my original response to you. Your unit tests don't need to test
that Tk is doing its job (hopefully the Tk people are doing that, but
seconding that, maybe your functional end-to-end tests are). Your unit
tests only need to test that you are making the right calls to the right
Tk methods.

Devin
 
H

Hidetoshi NAGAI

From: Joe Van Dyk <[email protected]>
Subject: Re: Test-Driven Development in GUIs
Date: Thu, 9 Jun 2005 07:59:31 +0900
Message-ID: said:
Tk question: How can I tell, via code, if a button is actually being
displayed on the screen?

Does TkWinfo.mapped?(win) or TkWindow#winfo_mapped? satisfy you?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top