Testing Things That Are Hard To Test

advertisement
Testing Things
That Seem Hard To Test
Robert Koss, Ph. D.
ObjectMentor, Inc.
koss@objectmentor.com
www.objectmentor.com
(c) 2002 Object Mentor, Inc.
1
Introduction
• XP dictates that we test everything that can possibly
break
– Subjective
• accessors and mutators
• What if mutator does validation?
• How much logic is too much?
• What if class Foo is difficult to test?
FooTest
Foo
(c) 2002 Object Mentor, Inc.
2
Sources of Difficulty
• Some objects appear to be difficult to test
–
–
–
–
Objects which depend upon other objects.
GUI’s
Databases
Servlets / EJB’s
• What makes these things hard to test?
– Not knowing what to test
– Source code dependencies
• Collaborating objects
(c) 2002 Object Mentor, Inc.
3
Knowing What to Test
• GUI
– Assume Swing / MFC works
– Model-View-Controller
• Make sure anything that can break is in a class which
can be tested
• Databases
– Assume Oracle / SQL Server, etc. works
• Servlets
– HttpUnit
– Delegate to a class which can be tested.
(c) 2002 Object Mentor, Inc.
4
Collaborating Objects
• Mark IV Coffee Maker
Warmer
+hasEmptyPot()
CoffeeMakerTest
CoffeeMaker
+ testMakeCoffee()
+makeCoffee()
Boiler
+ hasWater()
public void testMakeCoffee() {
CoffeeMaker cm = new CoffeeMaker();
cm.makeCoffee();
assert( ? ? ? );
}
(c) 2002 Object Mentor, Inc.
5
Breaking Troublesome
Dependencies
• A source-code dependency can always be broken by
the judicious use of an interface.
A
B
A
<<interface>>
• Dependency has been inverted
IB
B
– Both A and B now depend upon an interface
• A no longer knows concrete type of B
– Can now test A by supplying a MockB
(c) 2002 Object Mentor, Inc.
6
Mock Objects / Stubs
• Class A can’t tell if message goes to class B or class
BStub
• Class BStub used to create testing environment,
totally under control of class ATest
ATest
<<interface>>
A
<<creates>>
IB
BStub
(c) 2002 Object Mentor, Inc.
B
7
Self-Shunt Pattern
• Not always necessary to make a new class and
create a new object for Bstub
• Class ATest can implement the IB interface
ATest
A
<<interface>>
IB
B
(c) 2002 Object Mentor, Inc.
8
CoffeeMakerTest
• Stub out Warmer and Boiler
<<interface>>
Warme r
+hasEmptyPot()
CoffeeMakerTest
+
+
+
+
testPotWater()
testPotNoWater()
testNoPotWater()
testNoPotNoWater()
CoffeeMaker
+makeCoffee()
<<interface>>
Boiler
+ hasWater()
MarkIVBoiler
(c) 2002 Object Mentor, Inc.
9
Stubs Everywhere ?
• Stubs are good when real class can’t easily be used
– Hardware
– Database
– Dependent class not done
• Not necessary for Value Objects
• Not necessary if dependants are known to work
(c) 2002 Object Mentor, Inc.
10
Chains of Dependencies
• Bowling Game
10
Game
1, 2, 3
Frame
Throw
• Boom, Splash, (x, y, z, t)
Trigger
(c) 2002 Object Mentor, Inc.
11
More Uses for Stubs
• Logging
– When object under test is supposed to send a sequence of
messages to another object in a specific order
– Record message sent as a string
• append to string for each message
– Alternative to setting several flags and examining in test
(c) 2002 Object Mentor, Inc.
12
More Uses for Stubs
• Null Object Pattern
– When doing nothing is the right thing to do
– Don’t have to test for presence of Logger object
Class
Tested
A Lot
<<interface>>
Logge r
NullLogger
(c) 2002 Object Mentor, Inc.
FileLogger
13
Testing GUI’s
• Test Model (MVC) first
– Concentrate on business instead of user interface
– Start just inside the UI
– Quicken
• addCheck( new Check( checkNum, payee, amount)) is
more important than a fancy check gui
• Same with reconcile()
(c) 2002 Object Mentor, Inc.
14
Testing GUI’s
• Decide what to test
– Can test everything except for aestetics
– Should we?
• http://users.vnet.net/wwake/xp/xp0001/
public void testWidgetsPresent() {
SearchPanel panel = new SearchPanel();
assertNotNull(panel.searchLabel);
assertNotNull(panel.queryField);
assertNotNull(panel.findButton);
assertNotNull(panel.resultTable);
}
(c) 2002 Object Mentor, Inc.
15
Being Pragmatic
• Assume GUI toolkit works
– Swing, MFC, Qt, GTK
– new Button( “OK”);
• Continually ask if code being written can possibly
break
• Continually ask if code being written is doing more
than UI
• Make sure all logic is in classes that can be tested
• Make GUI code so thin that it can’t possibly break.
(c) 2002 Object Mentor, Inc.
16
Separate Domain From User Interface
• Model - View - Controller
• Document - View
– Combines traditional View with Controller
– Good unless several Controllers are needed
• Model - View - Presenter
– http://www106.ibm.com/developerworks/library/mvp.html
– Combines traditional View with Controller
– Adds another layer between UI and Model
– Presenter is ideal place to do unit testing of GUI
(c) 2002 Object Mentor, Inc.
17
Single Responsibility Principle
• A class has a single responsibility. It meets that
responsibility, the whole responsibility, and nothing
but that responsibility
– Called cohesion in Structured Design
• Applied to functions
– Rule of thumb
• Describe class’s responsibility without using “and”
• What is the responsibility of an event handler?
– “Handles” event
– Scope of “handle” subjective
– Should not have any program logic
(c) 2002 Object Mentor, Inc.
18
A Stupid GUI Is an Unbreakable GUI
• Event handler should do nothing but gather the
information from event and delegate responsibility
to another class
– Can’t possibly break
– e.g., MouseEventHandler should get click coordinates
and delegate to another class that knows what to do when
the mouse is clicked
• hit testing
(c) 2002 Object Mentor, Inc.
19
Event Handlers
• Dialog / Window with GUI Control and registered
listener
Some Window
Control 1
<<a nonym ous >>
<<interface>>
Listener 1
• Testing listener can be
– Easy / Annoying
– Hard / Impossible
(c) 2002 Object Mentor, Inc.
How do we test this?
20
Event Handlers Delegate
• Have event handler delegate to something that can
be tested
Some Window
Control 1
<<a nonym ous >>
<<interface>>
Listener 1
<<delegates>>
M ode lFacade
Presenter (MVP)
M ode lFacade
T e st
(c) 2002 Object Mentor, Inc.
21
General Architecture
• Note the direction of the dependencies
GUI
Toolkit
UI
Make this
unbreakable
Model
Facade
Model
Model
Facade
Test
Model
Test
(c) 2002 Object Mentor, Inc.
22
Interacting GUI Components
• Interaction with one GUI component causes
changes to other components.
– Menus becoming active / inactive
– Toolbar changes
• Mediator Pattern
– Have each event handler delegate to mediator and have
mediator determine what else must change
– Component interactions now localized (and testable!)
instead of being distributed over all event handlers.
(c) 2002 Object Mentor, Inc.
23
Mediators as State Machines
• Using a Mediator localizes inter-object
communication
• Mediators can become ugly for complicated
windows
• Implementing Mediator as a state machine makes it
easy to get the wiring correct and to easily
accommodate changes
(c) 2002 Object Mentor, Inc.
24
Summary
• We have to test everything that can possibly break
• If something appears hard to test, don’t let it do
anything that can break
– Delegate to an object that can be tested
(c) 2002 Object Mentor, Inc.
25
Download