Spring22-Lecture9-StructuralTesting
Spring22-Lecture9-StructuralTesting
Structural Testing
Gregory Gay
DIT635 - February 16, 2022
We Will Cover
• Test Adequacy Criteria
• Structural Testing:
• Use structural coverage to judge tests, create new tests.
• Statement, Branch, Condition, Path Coverage
2
Every developer must answer:
Are our tests are any good?
3
Have We Done a Good Job?
What we want:
• We’ve found all the faults.
What we (usually) get:
• We compiled and it worked.
• We run out of time or budget.
• Inadequate.
4
Test Adequacy Criteria
Can we compromise between
the impossible and the inadequate?
5
(In)Adequacy Criteria
• Criteria identify inadequacies in the tests.
• If no test reaches a statement, test suite is inadequate for
finding faults in that statement.
• If we plant a fake fault and no test exposes it, our tests
are inadequate at detecting that fault.
• Tests may still miss faults, but maximizing criteria
shows that tests at least meet certain goals.
6
Adequacy Criteria
• Adequacy Criteria based on coverage of factors
correlated to finding faults (hopefully).
• Exercising elements of source code (structural testing).
• Detection of planted faults (fault-based testing)
• Widely used in industry - easy to understand, cheap
to calculate, offer a checklist.
• Enable tracking of “testing completion”
• Can be measured in IntelliJ, Eclipse, etc.
7
Use of Criteria Test Inputs
8
Structural Testing
9
Structural Testing
• The structure of software is int[] flipSome(int[] A, int N, int X)
{
valuable information. int i=0;
while (i<N and A[i] <X)
• Prescribe how code elements {
if (A[i] < 0)
should be executed, and measure A[i] = - A[i];
coverage of execution. i++;
}
• If-statements, Boolean expressions, return A;
}
loops, switches, paths between
statements...
10
The basic idea:
You can’t find all of the
faults without exercising all
of the code.
11
Structural Testing - Motivation
• Requirements-based tests should execute most
code, but will rarely execute all of it.
• Helper functions.
• Error-handling code.
• Requirements missing outcomes.
• Structural testing compliments functional testing by
covering gaps in the source code.
12
Structural Does Not Replace Functional
• Should not be the basis for all test cases
• Harder to make verification argument.
• May not map directly to requirements.
• Does not expose missing functionality.
• Useful for supplementing functional tests.
• Functional tests good at exposing conceptual faults.
• Structural tests good at exposing coding mistakes.
13
Control and Data Flow
• We need to understand how system executes.
• Conditional statements result in branches in execution,
jumping between blocks of code.
• Control flow: how control passes through code.
• Which code is executed, and when.
• Data flow: how data passes through code.
• How variables are used in different expressions.
14
Control-Flow Graphs
• Directed graph representing
flow of control. i=0
• Nodes represent blocks of
sequential statements. i<N
• Edges connect nodes in the True
False A[i]<0
sequence they are executed. True
• Multiple edges indicate False A[i] = - A[i];
conditional statements. return(1)
i++
15
Control Flow: If-then-else
1 if (1==x) {
2 y=45; 1==x
3 } T F
4 else {
y=45; y=23456;
5 y=23456;
6 }
7 /* continue */ /* continue */
16
Loop
1 while (1<x) {
2 x--; 1<x
3 } T F
4 /* continue */
/* continue */
x--;
17
Case
test
1 switch (test) {
2 case 1 : ...
3 case 2 : ... case 1... case 2... case 3...
4 case 3 : ...
5 }
6 /* continue */ /* continue */
18
Basic Blocks
for(int i=0; i < 10; i++){
• Nodes are basic blocks. sum += i;
• Set of sequential }
instructions with single
int i = 0;
entry and exit point.
• Typically adjacent i < 10
statements, but one T F
sum += i;
statement might be broken i++;
up to model control flow in
the statement.
19
Control Flow Graph Example
1. public static String collapseNewlines(String argSt){ 1-3
2. char last = argStr.charAt(0);
3. StringBuffer argBuf = new StringBuffer();
4. int cldx = 0;
5. for(int cldx = 0; cldx < argStr.length(); cldx++){
6. char ch = argStr.charAt(cldx);
cldx <
7. if (ch != ‘\n’ || last != ‘\n’){ argStr.length();
8. argBuf.append(ch);
9. last = ch; F T
10. { 13 6
11. }
12.
13. return argBuf.toString();
7
F
14. } T
8-9 cldx++;
20
Structural Coverage Criteria
• Criteria based on exercising:
• Statements (nodes of CFG)
• Branches (edges of CFG)
• Conditions
• Paths
• … and many more
• Measurements used as adequacy criteria
21
Statement Coverage
• Most intuitive criteria. Did we execute every
statement at least once?
• Cover each node of the CFG.
• The idea: a fault in a statement cannot be revealed
unless we execute the statement.
• Coverage = Number of Statements Covered
Number of Total Statements
22
Statement Coverage
int[] flipSome(int[] A, int N, int X) i=0
{
int i=0;
while (i<N and A[i] <X) i<N and A[i] <X
{ True
if (A[i] < 0) False A[i]<0
A[i] = - A[i]; True
i++; False A[i] = - A[i];
}
return A; return A
} i++
23
A Note on Test Suite Size
• Coverage not correlated to test suite size.
• Coverage depends on whether obligations are met.
• Some tests might not cover new code.
• However, larger suites often find more faults.
• They exercise the code more thoroughly.
• How code is executed often more important than
whether it was executed.
24
Test Suite Size
• Favor large number of targeted tests over small
number of tests that cover many statements.
• If a test targets a small number of obligations, it is easier
to tell where a fault is.
• If a test covers a large number of obligations, we get
higher coverage, but at the cost of being able to identify
and fix faults.
• The exception - cost to execute each test is high.
25
Branch Coverage
• Do we have tests that take all of the control
branches at some point?
• Cover each edge of the CFG.
• Helps identify faults in decision statements.
• Coverage = Number of Branches Covered
Number of Total Branches
26
Subsumption
• Criterion A subsumes Criterion B if, for every
program P, every test suite satisfying A also
satisfies B on P.
• If we satisfy A, there is no point in measuring B.
• Branch coverage subsumes statement coverage.
• Covering all edges in CFG requires covering all nodes.
27
Subsumption
• Shouldn’t we always choose the stronger metric?
• Not always…
• Typically require more obligations.
• (so, you have to come up with more tests)
• Or, at least, tougher obligations.
• (making it harder to come up with the test cases).
• May end up with unsatisfiable obligations.
28
Branch Coverage
int[] flipSome(int[] A, int N, int X) i=0
{
int i=0;
while (i<N and A[i] <X) i<N and A[i] <X
{ True
if (A[i] < 0) False A[i]<0
A[i] = - A[i]; True
i++; False A[i] = - A[i];
}
return A; return A
} i++
30
Decisions and Conditions
• A decision is a complex Boolean expression.
• Often cause control-flow branching:
• if ((a && b) || !c) { ...
• But not always:
• Boolean x = ((a && b) || !c);
31
Decisions and Conditions
• A decision is a complex Boolean expression.
• Made up of conditions
• Connected with Boolean operators (and, or, xor, not):
• Boolean variables: Boolean b = false;
• Subexpressions that evaluate to true/false involving (<, >, <=, >=,
==, and !=): Boolean x = (y < 12);
32
Decision Coverage
• Branch Coverage deals with a subset of decisions.
• Branching decisions that decide how control is routed
through the program.
• Decision coverage requires that all boolean
decisions evaluate to true and false.
• Coverage = Number of Decisions Covered
Number of Total Decisions
33
Basic Condition Coverage
• Several coverage metrics examine the individual
conditions that make up a decision.
• Identify faults in decision statements.
(a == 1 || b == -1) instead of (a == -1 || b == -1)
35
Basic Condition Coverage
int[] flipSome(int[] A, int N, int X) i=0
{
int i=0;
while (i<N and A[i] <X) i<N and A[i] <X
{ True
if (A[i] < 0) False A[i]<0
A[i] = - A[i]; True
i++; False A[i] = - A[i];
}
return A; return A
} i++
● ([-1, 1], 2, 10)
○ Negative value in array
○ Positive value (but < X)
● ([11], 1, 10)
○ Positive, but > X
● Both eventually cause i < N to be false.
36
Compound Condition Coverage
• Evaluate every combination of the conditions
Test Case A B
1 True True
(A and 4
5
True
True
True
False
False
True
False
True
6 True False True False
(B and 7
8
True
True
False
False
False
False
True
False
9 False True True True
(C and 10
11
False
False
True
True
True
False
False
True
12 False True False False
D)))) 13
14
False
False
False
False
True
True
True
False
15 False False False True
16 False False False False
38
Short-Circuit Evaluation
• In many languages, if the first condition determines
the result of the entire decision, then fewer tests are
required.
• If A is false, B is never evaluated.
Test Case A B
1 True True
(A and B) 2 True False
3 False -
39
Modified Condition/Decision Coverage(MC/DC)
• Requires:
• Each condition evaluates to true/false
• Each decision evaluates to true/false
• Each condition shown to independently affect outcome
of each decision it appears in.
Test Case A B (A and B)
1 True True True
2 True False False
3 False True False
4 False False False
40
Activity https://bit.ly/34OYvjq
Draw the CFG and write tests that provide statement, branch,
and basic condition coverage over the following code:
public int search(String[] A, String what){
int index = 0;
if ((A.length == 1) && (A[0] == what)){
return 0;
} else if (A.length == 0){
return -1;
} else if (A.length > 1){
while(index < A.length){
if (A[index] == what){
return index;
} else
index++;
}
}
}
return -1;
}
41
Activity - Control Flow Graph
index=0
False False
A.length
False A.length
(A.length ==1)
==0 >1 return -1;
&& (A[0] = what)
True False
True index <
True
A.length
return 0; return -1; True
A[index] False
== what
index++;
42
Activity - Possible Solution
index=0
43
Loop Boundary Coverage
• Focus on problems related to loops.
• Cover scenarios representative of how loops might be
executed.
• For each loop, write tests that:
• Skip the loop entirely.
• Take exactly one pass through the loop.
• Take two or more passes through the loop.
44
Nested Loops
• Often, loops are nested within other loops.
• For each level, execute 0, 1, 2+ times
• In addition:
• Test innermost loop first with outer loops executed
minimum number of times.
• Move one loops out, keep the inner loop at “typical”
iteration numbers, and test this layer as you did the
previous layer.
• Continue until the outermost loop tested.
45
Concatenated Loops
• One loop executes. Next line of code starts a new
loop. These are generally independent.
• If not, follow a similar strategy to nested loops.
• Start with bottom loop, hold higher loops at minimal iteration
numbers.
• Work up towards the top, holding lower loops at “typical”
iteration numbers.
46
Why These Loop Strategies?
• If proving correctness, we establish preconditions,
postconditions, and invariants that are true on each
execution of loop.
• The loop executes zero times when the postconditions
are true in advance.
• The loop invariant is true on loop entry (one), then each
loop iteration maintains the invariant (many).
• (invariant and !(loop condition) implies postconditions are met)
48
Activity: Binary Search
1-5 Tests that execute the loop:
● 0 times key = 1, T = [1]
● 1 time key = 2, T = [1, 2]
7 ● 2+ times key = 3, T = [1, 2, 3]
T F
8 9
F
25 13
F T 19
T
18
14 15 F
21
16
T
49
The Infeasibility Problem
Sometimes, no test can satisfy an obligation.
• Impossible combinations of conditions.
• Unreachable statements as part of defensive
programming.
• Error-handling code for conditions that can’t occur.
• Dead code in legacy applications.
50
The Infeasibility Problem
• Stronger criteria call for potentially infeasible
combinations of elements.
51
The Infeasibility Problem
• Adequacy “scores” based on coverage.
• 95% branch coverage, 80% MC/DC coverage, etc.
• Stop once a threshold is reached.
• Unsatisfactory solution - elements are not equally
important for fault-finding.
• Manual justification for omitting each impossible
test obligation.
• Helps refine code and testing efforts.
• … but very time-consuming.
52
Which Coverage Criterion Should I Use?
Basic Condition
Branch Coverage
Coverage
Power,
Cost Statement Coverage
53
We Have Learned
• Test adequacy metrics “measure” how good our
tests are.
• Prescribe test obligations that remove inadequacies from
test suites.
• Code structure is used in many adequacy criteria.
• Criteria, based on statements, branches,
conditions, loops, etc.
54
Next Time
• Next class: Structural coverage and data-flow
• Optional Reading - Pezze and Young, Chapters 6 and 13
• Friday Exercise Session: Structural Coverage
• Using Meeting Planner code
55