Introduction to Software Testing
Graph Coverage for Design
Elements
OO Software and Designs
• Emphasis on modularity and reuse puts complexity in the
design connections
• Testing design relationships is more important than before
• Graphs are based on the connections among the software
components
• Connections are dependency relations, also called couplings
I
Call Graph
• The most common graph for structural design testing
• Nodes : Units (in Java – methods)
• Edges : Calls to units
A Node coverage : call every unit at least once
(method coverage)
B C D
E F Edge coverage : execute every call at least
once (call coverage)
Example call graph
I
Call Graphs on Classes
• Node and edge coverage of class call graphs often do not work very well
• Individual methods might not call each other at all!
Class stack
public void push (Object o)
public Object pop ( ) ???
public boolean isEmpty (Object o)
push pop isEmpty
Other types of testing are needed – do not use graph criteria
I
Inheritance & Polymorphism
Caution : Ideas are preliminary and not widely used
A
Classes are not executable, so
this graph is not directly testable
B
We need objects
C D A
a
Example inheritance
hierarchy graph B objects
b
C D What is coverage
c d on this graph ?
I
Coverage on Inheritance Graph
• Create an object for each class ?
• This seems weak because there is no execution
• Create an object for each class and apply call coverage?
OO Call Coverage : TR contains each reachable node in the call
graph of an object instantiated for each class in the class
hierarchy.
OO Object Call Coverage : TR contains each reachable node in
the call graph of every object instantiated for each class in the
class hierarchy.
• Data flow is probably more appropriate …
I
Data Flow at the Design Level
• Data flow couplings among units and classes are more complicated than
control flow couplings
• When values are passed, they “change names”
• Many different ways to share data
• Finding defs and uses can be difficult – finding which uses a def can reach is very
difficult
• When software gets complicated … testers should get interested
• That’s where the faults are!
• Caller : A unit that invokes another unit
• Callee : The unit that is called
• Callsite : Statement or node where the call appears
• Actual parameter : Variable in the caller
• Formal parameter : Variable in the callee
I
Example Call Site
A Caller
B (x) Actual
Parameter
interface end A
Callee
B (Y)
Formal
Parameter
end B
• Applying data flow criteria to def-use pairs between units is too expensive
• Too many possibilities
• But this is integration testing, and we really only care about the interface …
I
Inter-procedural DU Pairs
• If we focus on the interface, then we just need to consider the last
definitions of variables before calls and returns and first uses inside
units and after calls
• Last-def : The set of nodes that define a variable x and has a def-clear
path from the node through a callsite to a use in the other unit
• Can be from caller to callee (parameter or shared variable) or from callee to
caller as a return value
• First-use : The set of nodes that have uses of a variable y and for
which there is a def-clear and use-clear path from the callsite to the
nodes
I
Example Inter-procedural DU Pairs
Caller
1 x=5
F X = 14 last-def
10 B (int y)
y = G (x) callsite 2 x=4
DU pair
print (y) 11 Z = y 12 T = y
first-use 3 x=3
Callee 13 print (y)
4 B (x)
G (a) print (a)
first-use
DU Last Defs
pair b = 42 2, 3
last-def
return (b) First Uses
11, 12
I
Example – Quadratic
25 ok = Root (X, Y, Z);
1 // Program to compute the quadratic root for 26 if (ok)
two numbers 27 System.out.println
2 import java.lang.Math; 28 (“Quadratic: ” + Root1 + Root2);
3 29 else
4 class Quadratic 30 System.out.println (“No Solution.”);
5{ 31 }
6 private static float Root1, Root2; 32
7 33 // Three positive integers, finds quadratic root
8 public static void main (String[] argv) 34 private static boolean Root (int A, int B, int C)
9 { 35 {
10 int X, Y, Z; 36 float D;
11 boolean ok; 37 boolean Result;
12 int controlFlag = Integer.parseInt (argv[0]); 38 D = (float) Math.pow ((double)B,
13 if (controlFlag == 1) (double2-4.0)*A*C );
14 { 39 if (D < 0.0)
15 X = Integer.parseInt (argv[1]); 40 {
16 Y = Integer.parseInt (argv[2]); 41 Result = false;
17 Z = Integer.parseInt (argv[3]);
18 } 42 return (Result);
19 else 43 }
20 { 44 Root1 = (float) ((-B + Math.sqrt(D))/(2.0*A));
45 Root2 = (float) ((-B – Math.sqrt(D))/(2.0*A));
21 X = 10; 46 Result = true;
22 Y = 9; 47 return (Result);
23 Z = 12; 48 } / /End method Root
24 } 49
I 50 } // End class Quadratic
1 // Program to compute the quadratic root for two numbers
2 import java.lang.Math;
3
4 class Quadratic
5{
6 private static float Root1, Root2; shared variables
7
8 public static void main (String[] argv)
9 {
10 int X, Y, Z;
11 boolean ok;
12 int controlFlag = Integer.parseInt (argv[0]);
13 if (controlFlag == 1)
14 {
15 X = Integer.parseInt (argv[1]);
last-defs 16 Y = Integer.parseInt (argv[2]);
17 Z = Integer.parseInt (argv[3]);
18 }
19 else
20 {
21 X = 10;
22 Y = 9;
23 Z = 12;
24 }
I
25 ok = Root (X, Y, Z);
first-use 26 if (ok)
27 System.out.println
28 (“Quadratic: ” + Root1 + Root2);
29 else
30 System.out.println (“No Solution.”);
31 }
32
33 // Three positive integers, finds the quadratic root
34 private static boolean Root (int A, int B, int C)
first-use 35 {
36 float D;
37 boolean Result;
38 D = (float) Math.pow ((double)B, (double2-4.0)*A*C);
39 if (D < 0.0)
40 {
last-def 41 Result = false;
42 return (Result);
43 }
44 Root1 = (float) ((-B + Math.sqrt(D)) / (2.0*A));
last-defs 45 Root2 = (float) ((-B – Math.sqrt(D)) / (2.0*A));
46 Result = true;
47 return (Result);
48 } / /End method Root
49
50 } // End class Quadratic
I
Quadratic – Coupling DU-pairs
Pairs of locations: method name, variable name, statement
(main (), X, 15) – (Root (), A, 38)
(main (), Y, 16) – (Root (), B, 38)
(main (), Z, 17) – (Root (), C, 38)
(main (), X, 21) – (Root (), A, 38)
(main (), Y, 22) – (Root (), B, 38)
(main (), Z, 23) – (Root (), C, 38)
(Root (), Root1, 44) – (main (), Root1, 28)
(Root (), Root2, 45) – (main (), Root2, 28)
(Root (), Result, 41) – ( main (), ok, 26 )
(Root (), Result, 46) – ( main (), ok, 26 )
I
Coupling Data Flow Notes
• Only variables that are used or defined in the callee
• Implicit initializations of class and global variables
• Transitive DU-pairs are too expensive to handle
– A calls B, B calls C, and there is a variable defined in A and used in C
• Arrays : a reference to one element is considered to be
a reference to all elements
I
Inheritance, Polymorphism & Dynamic Binding
• Additional control and data connections make data flow analysis more
complex
• The defining and using units may be in different call hierarchies
• When inheritance hierarchies are used, a def in one unit could
reach uses in any class in the inheritance hierarchy
• With dynamic binding, the same location can reach different
uses depending on the current type of the using object
• The same location can have different definitions or uses at
different points in the execution !
I
Additional Definitions
• Inheritance : If class B inherits from class A, then all variables and methods in
A are implicitly in B, and B can add more
• A is the parent or ancestor
• B is the child or descendent
• An object reference obj that is declared to be of type A can be assigned an
object of either type A, B, or any of B’s descendents
• Declared type : The type used in the declaration: A obj;
• Actual type : The type used in the object assignment: obj = new B();
• Class (State) Variables : The variables declared at the class level, often private
I
Types of Def-Use Pairs
def
def last-def
A () A ()
use
inter-procedural
B () first-use data flow
A () use B ()
intra-procedural data flow
(within the same unit) full coupling
def
A() M () A() def
A()
M() A()
B() use N()
B() use
B()
N() B()
F () F()
object-oriented direct object-oriented indirect
I
coupling data flow coupling data flow
OO Data Flow Summary
• The defs and uses could be in the same class, or
different classes
• Researchers have applied data flow testing to the
direct coupling OO situation
• Has not been used in practice
• No tools available
• Indirect coupling data flow testing has not been tried
either in research or in practice
• Analysis cost may be prohibitive
I
Web Applications and Other Distributed Software
P1 message P2
def use
A() B()
distributed software data flow
• “message” could be HTTP, RMI, or other mechanism
• A() and B() could be in the same class or accessing a
persistent variable such as in a web session
• Beyond current technologies
I
Summary—What Works?
• Call graphs are common and very useful ways to
design integration tests
• Inter-procedural data flow is relatively easy to
compute and results in effective integration tests
• The ideas for OO software and web applications are
preliminary and have not been used much in
practice