Chapter 15 Object-Oriented Testing Testing in OO Environment • The reasons for testing is not any different for any of the design and implementation methodologies, including OO methodology. – Find faults and fixing them before releasing – Show that the software works (at least in chosen areas) • In OO we are especially interested in testing because we want to “reuse” the tested objects. • Does our approach have to change because of OO? – Not really - - - but we do have to focus on a few characteristics Issues in Testing Object-Oriented Software • How is object-oriented (o-o) software different/special? – Need to clarify unit – Composition vs. decomposition – No reason for integration testing based on functional decomposition tree – Can still use the (better) idea of Call Graph based integration • Properties of an o-o programming language – Inheritance – Encapsulation – Polymorphism • Message communication – Message quiescence – Event quiescence Basic Levels of Testing in OO Same as before; testing the whole system System Test Unit A Integration test Unit X Integration test Unit Testing Same as before; testing “related” units as a group What is a “unit” In OO? Units in Object-Oriented Software • Guidelines for units – A unit is the smallest software component that can be compiled and executed. (traditional view, usually in procedural code) – A unit is a software component that would never be assigned to more than one designer to develop. (Project management view, appropriate for both procedural and o-o software) – Unit developer does detailed design, coding, and unit level testing • Candidate o-o units – A single method – A class (generally accepted view) Single Methods as Units • Appropriate for large classes • Consistent with the one designer/one unit practice • Single method testing – reduces to unit testing for procedural units – o-o units are typically less complex (cyclomatic) than procedural units • This choice mandates “intra-class” integration testing. Class and Object instantiation • There are at least two concerns with viewing class as a “unit”: 1. We should view Class as a fundamental unit and test it as an independent entity by itself. • scaffolding 2. integration What and how much “scaffolding” code do we need to write to invoke and test this Class. – – e.g. Class1 x1; - - - You may need to write the public static void “main” method to test the class (possible scaffolding code) If there is a large method inside the Class and we know (via white-box testing approach) that the method invokes other methods in other Classes, should we follow (test) the thread of methods across different Classes? – the module-module (mm-path) approach may be employed for testing the “thread” of methods, that may go beyond one Class 3 important concepts in OO 1. Encapsulation: – – – 2. In OO, the notion of self contained and only know/operate within its own domain is very important Good encapsulation gives rise to good reusable classes that can be composed with other classes to deliver more service. We are also after highly cohesive and loosely coupled units with encapsulation. Inheritance: – A way to gain on the concept of reuse through a class taking (inheriting) portions from a super class. Overload/Polymorphism” 3. – Another way to gain on the reuse concept through using parts of class or method for different purposes based on the passed parameter or based on run time usage (late binding). How do these concepts affect us in testing? Encapsulation • Both data and methods may be encapsulated and protected. Depending on the programming language used there may be slight differences in implementing the encapsulation mechanism: – Public (visible) - - - pretty much same in all languages – Private (hidden) - - - only methods in the class can access – Protected (secret)- - - subclass methods can access • Test the access of encapsulated data • Test the access of encapsulated methods Read the discussion on the positions of “dial’ and “lever” of the windshield wiper example on page 287. The exchange of “positions” of these two may be different depends on the design and how the data is encapsulated. Inheritance Example (JAVA) • One may want to test the methods in the inherited class which were previously tested in the superclass. • An example is a method, m1, that only returns positive integers and is used in another method, m2, as a divisor. An inherited subclass modifies (over rides) m1 to allow all integers, including zero. (We will need to ensure that the inherited and previously tested m2 is retested). public class Example { int y; public int m1 { y = (test * test) +1; return y; } public void m2 { z=w/y; } import Example ; public class Example2 extends Example { public int m1 { y = test * test ; return y; } } Similar Inheritance Problem Example (C++) Class Test1 { int some_variable; . Original CLASS . int method1 ( ) { return 1; } int method2 ( ) { return 1 / method1( ); } } Class Test_child : Public Test1 { int mehtod1 ( ) { return 0; } } Inherited CLASS over-riding method1 Inheritance • Do we need to test multiple inheritance, where there may be conflicts in inheritance? – Two subclasses, Sub1 and Sub2, are inherited from a superclass, S1, and they both modify the method, method1, but differently. If we further inherit from both Sub1 and Sub2, what should we expect method1 to be like? Mother Varx : int method1 ( ) method2 ( ) Sibling 1 Note: JAVA does not allow multiple inheritance Sibling 2 (method1) (method1) overridden Overridden differently Grand_Sibling 1 Inheritance • Inheritance provides us with the ability to reuse (and save both implementation and testing expenses) what is there and make incremental changes by creating a subclass by either adding to or modifying the : – – • Instance variable Methods Using “Flattened Class” concept: 1. 2. 3. One may want to test subclass access of the “encapsulated” data or methods in the super-class. One may want to test all the methods in the inherited class which were previously tested in the super-class. • An example is a method, m1, that only returns positive integers and is used in another method, m2, as a divisor. An inherited subclass modifies m1 to allow all integers, including zero. (We will need to ensure that the inherited and previously tested m2 is retested). Do we need to test multiple inheritance, where there may be conflicts in inheritance? • Two subclasses, Sub1 and Sub2, are inherited from a super-class, S1, and they both modify the method, m1, but differently. If we further inherit from both Sub1 and Sub2, what should we expect m1 to be like? Testing flattened Classes of all inherited subclasses will cause duplication of testing the same (non-overridden) methods that appear in all the inherited subclasses Implications of Inheritance Account accountNumber Balance getBalance() setBalance() checkingAccount savingsAccount checkProcessingCharge checkNumber interestRate postCharge() postInterest() Implications of Inheritance—Flattened Classes checkingAccount savingsAccount accountNumber Balance checkProcessingCharge checkNumber accountNumber Balance interestRate getBalance() setBalance() postCharge() getBalance() setBalance() postInterest() Example—O-O Windshield Wiper Class lever(leverPosition; private senseLeverUp(), private senseLeverDown() ) Class dial(dialPosition; private senseDialUp(), private senseDialDown() ) Class wiper(wiperSpeed; setWiperSpeed(newSpeed) ) Which unit controls the interaction between the Lever and the Dial? Saturn Windshield Wiper Objects Wiper Speed Lever Position Dial Position Compute Speed Lever Dial position position Sense Up Sense Down Sense Left Sense Right Windshield Wiper Class Behavior Lever Dial Wiper Off 1 0 wipes/minute leverDown leverUp 2 Int leverDown leverUp dialDown dialUp 3 Low 6 wipes/minute dialDown dialUp 12 wipes/minute leverDown leverUp High 20 wipes/minute 30 wipes/minute 60 wipes/minute Unit Testing for Windshield Wiper Classes • Can test only the Lever and Dial classes directly. • Test cases to verify that event-oriented methods make correct changes to the position attributes. • Need “mock objects” (o-o analog of drivers for procedural code) to provide inputs to test the wiper class. • Note how the “line” between unit and integration testing is blurred! Sample Unit Test Case for Lever Test Case Name Test Sense Up method Test Case ID Lever-1 Description Verify that the LeverUp method correctly changes the value of the position attribute Pre-Conditions 1. position = Off Input(s) Expected Output(s) 1. move lever to Int 2. position = Int 3. move lever to Low 4. position = Low 5. move lever to High 6. position = High Post condition(s) 1. position = High Test Result? Pass/Fail Test operator Paul Jorgensen Test Date Dec. 8, 2013 Windshield Wiper StateChart Lever Wiper Off 0 wipes/minute leverDown InState(Int) leverUp InState(Off) Int leverDown InState(1) leverUp Low leverDown 6 wipes/minute InState(2) InState(1) leverUp High InState(2) 12 wipes/minute InState(3) Dial 1 dialUp dialDown 2 dialUp InState(3) 20 wipes/minute InState(Low) dialDown InState(2) InState(Int) 30 wipes/minute InState(High) InState(Low) 3 60 wipes/minute StateChart-Based Testing for the Windshield Wiper • Need an engine to execute the StateChart – execute “interesting” scenarios – save paths as skeletons of system test cases • The InState events show how the Wiper class can be tested once the Lever and Dial classes have been tested. Sample System Level Test Case Test Case Name Exercise all wiper speeds Test Case ID WindshieldWiper-1 Description The windshield wiper is in the OFF position, and the Dial is at the 1 position; the user moves the lever to INT, and then moves the dial first to 2 and then to 3; the user then moves the lever to LOW.; the user moves the lever to High. 1. The Lever is in the OFF position Pre-Conditions 2. The Dial is at the 1 position 3. The wiper speed is 0 Input events Output events 1. move lever to INT 2. speed is 6 3. move dial to 2 4. speed is 12 5. move dial to 3 6. speed is 20 7. move lever to LOW 8. speed is 30 9. move lever to HIGH 10. speed is 60 1. The Lever is in the HIGH position Post conditions 2. The Dial is at the 3 position 3. The wiper speed is 60 Polymorphism and Late Binding (in C++) • Polymorphism and late binding is a special feature that allows us to have multiple behavior based on which class a statement is bound to. e.g. in C++ Class Cube{ protected: float length; public: virtual float area( ) {return (length* length);} // area of square void set_length(float z) {length = z} // set passed parameter, length void volume( ) {cout<< “volume is” << area( ) * length;} // the area( ) method is not bound }; Class Cylinder: public Cube { virtual float area( ) {return (3.14* length**2);} // area of circle } . . Cube c1, Cylinder cyl1; . c1.volume( ); // give us the volume of cube cyl1.volume( ); // give us the volume of circle •Both classes must be tested, especially if one of them is a division by length. Polymorphism via dynamic instantiation (in JAVA) class Shape { - - - - - - - } class Rectangle extends Shape { - - - - } class Triangle extends Shape { - - - - } class Circle extends shape { - - - - } import Rectangle; import Triangle; import Circle; public class Example { public static void main ( String[ ] args) String S = get_shape_type ( ) ; // some method that reads and returns a string to S Shape myFigure; myFigure = (Shape) java.lang.class.forName(S).newinstance( ); /* myFigure is instantiated to what the string read in --- hopefully it is rectangle, circle or triangle */ /* Note that it is “casted’ as type Shape, which is the parent Class */ } Is equivalence class testing of valid vs invalid shape good enough? Do you need to test everyone of the valid shape? Polymorphism via “interface” (in JAVA) public interface Speaker { --public void speak( ); } public class cat implements Speaker { --public void speak ( ) { system.out.println(“meow”); } } public class dog implements Speaker { --public void speak ( ) { system.out.println(“woof woof”);} } Import cat; Import dog; Public class AnimalTalk { public static void main ( string[ ] arg) { Speaker current; system.out.println(“enter 1 or 2”); /* 1 = cat and 2 = dog */ int j = keyboard.readInt ( ); if (j == 1) current = new cat ( ); else current = new dog ( ); current.speak ( ); } } All Branch Test will take care of both cat and dog ?----- Basic Levels of Testing in OO Same as before; testing the whole system System Test Unit A Integration test Unit X Integration test Unit Testing Test relations and Inheritance; Encapsulation; Polymorphism Test class and class Inheritance Integration Testing • Assumes the unit or units are unit-tested. • Use the collaboration diagram and the sequence diagram from UML to design the integration test cases. 1 2 3 Collaboration diagram where boxes are classes and numbered arrows indicate sequenced messages Sequence diagram where the boxes are classes and the arrows indicate the sequence of messages Testing Object-Oriented NextDate • • • • Example—our familiar NextDate Rewritten here in object-oriented style One Abstract Class—CalendarUnit Classes – – – – – testIt Date Day Month Year • (See text for pseudo-code) • Program graphs in next few slides Program Graphs of Date Methods testIt Date.constructor Date.increment 1 4 8 2 5 9 3 6 10 11 7 12 15 13 16 Date.printDate 19 20 14 17 18 Program Graphs of Day Methods Day.constructor Day.setDay Day.increment 21 23 28 22 24 29 25 30 Day.setCurrentPos Day.getDay a 26 b 27 32 31 33 Program Graphs of Month Methods Month.constructor Month.setCurrentPos Month.getMonth 34 a 39 35 b 40 Month.setMonth Month.getMonthsize boolean.increment 36 41 47 37 42 48 38 44 43 45 46 49 51 50 52 Program Graphs of Year Methods Year.setCurrentPos Year.constructor Year.getYear a 53 55 b 54 56 boolean.increment boolean.isLeap 57 60 58 61 59 63 62 64 ooNextDate Collaboration Diagram 1. create 2. increment 3. getYear Year 1. create 2. increment 3. getMonth 4. setMonth 1. create 2. increment 3. printDate testIt Month Date 1. create 2. increment 3. getDay 4. setDay Day printDate Sequence Diagram testIt Date .printDate 1. printDate Day d.setDay Month m.setMonth 2. setDay 3. setMonth Time 4. setYear Year y.setYear MM-Paths for O-O Software • Definition: An object-oriented MM-Path is a sequence of method executions linked by messages. • Call Graph slightly revised – nodes are methods – edges are messages • Next three slides show sample Call Graphs for ooNextDate All MM-Paths as a Call Graph Day Day() setCurrentPos() msg6 increment() msg16 msg15 testIt Main msg7 setDay() getDay() msg11 msg13 msg1 msg2 msg3 Month Month() Date msg18 msg5 setCurrentPos() msg17 getDate() msg8 increment() increment() msg10 printDate() msg12 msg19 setMonth() getMonth() getMonthSize() msg4 Year msg9 Year() setCurrentPos() msg14 increment() setYear() getYear() msg21 msg20 MM-Path for January 3, 2013 Day Day() setCurrentPos() msg6 increment() msg16 msg15 testIt setDay() Main getDay() Month msg1 Month() Date msg18 msg5 setCurrentPos() getDate() increment() increment() msg19 setMonth() printDate() getMonth() getMonthSize() msg4 Year Year() setCurrentPos() increment() setYear() getYear() msg21 MM-Path for April 30, 2013 Day Day() setCurrentPos() increment() testIt Main msg7 setDay() getDay() msg11 Month Month() msg2 Date msg5 setCurrentPos() getDate() increment() increment() msg17 setMonth() printDate() getMonth() getMonthSize() msg4 Year Year() setCurrentPos() increment() setYear() getYear() msg20 Integration Test Coverage Metrics • Coverage metrics with respect to Call Graph • Given a set of test cases that – covers each method (node coverage) – covers each message (edge coverage) – covers each path System Testing for O-O Software • Nearly equivalent to system testing for procedural software. • Some possibilities for sources of system test cases... – Use cases – Model-Based system testing Second Example (event-driven system testing) Currency Converter U.S. Dollar amount Equivalent in ... Brazil Compute Canada Clear European community Japan Quit Second Example (continued) label1 Currency Converter frmCurrConv label2 U.S. Dollar amount txtDollar label3 Equivalent in ... lblEquivAmount optBrazil Brazil optCanada optEU optJapan Compute cmdCompute Canada Clear cmdClear Quit cmdQuit European community Japan Input Events for the Currency Converter Input Events Input Events ip1 Enter US Dollar amount ip2.4 Click on Japan ip2 Click on a country button ip3 Click on Compute button ip2.1 Click on Brazil ip4 Click on Clear button ip2.2 Click on Canada ip5 Click on Quit button ip2.3 Click on European Community ip6 Click on OK in error message Output Events for the Currency Converter Output Events Output Events op1 Display US Dollar Amount op4 Reset selected country op2 Display currency name op4.1 Reset Brazil op2.1 Display Brazilian Reals op4.2 Reset Canada op2.2 Display Canadian Dollars op4.3 Reset European Community op2.3 Display European Community Euros op4.4 Reset Japan op2.4 Display Japanese Yen op5 Display foreign currency value op2.5 Display ellipsis op6 Error Msg: Must select a country op3 Indicate selected country op7 Error Msg: Must enter US Dollar amount op3.1 Indicate Brazil op8 Error Msg: Must select a country and enter US Dollar amount op3.2 Indicate Canada op9 Reset US Dollar Amount op3.3 Indicate European Community op10 Reset equivalent currency amount op3.4 Indicate Japan StateChart for the Currency Converter In Storage ip 5 Quit Executing End Application Ip 4 Idle Ip4 ___________________ op 2, op 4, op 9 , op 10 Ip 3/op 8 GUI action Ip 2/ op 2 ,op 3 Country Selected Ip 6 Ip 1/op 1 U .S. Dollar Amount Selected Missing Country and Dollar Message Ip 1/op 1 Ip 2/op 2,op 3 Ip 3/op 7 Ip6 Ip 6 Both Inputs Done Missing U .S. Dollar Message Missing Country Message Ip 3/op 5 Ip1 /op 1 Ip 2/op 2,op 3 Equiv . Amount Displayed Ip 3/ op 7 Sample System Level Test Case System test Case -3 Normal usage (dollar amount entered first) Test performed by Paul Jorgensen Pre-Conditions txtDollar has focus Input events Output events 1. Enter 10 on the keyboard 2. Observe 10 appears in txtDollar 3. Click on optEU button 5.Click cmdCompute button 4. Observe “euros” appears in label3 6. Observe “7.60” appears in lblEquivAmount Post-Conditions cmdClear has focus Test result Pass (on first attempt) Date run May 27, 2013