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