08 Testing
08 Testing
08: Testing
Why test?
• Mars Climate Orbiter
‒ Purpose: to study the Martian
climate and to serve as a relay for
the Mars Polar Lander
2
Why test?
• THERAC-25 Radiation Therapy
‒ 1985 to 1987: two cancer patients at the East Texas
Cancer Center in Tyler received fatal radiation overdose
(a total of 6 accidents) – massive overdose
‒ Why: Software bug - mishandled race condition (i.e.,
miscoordination between concurrent tasks)
3
Why test?
• Hive Thermostat
• February, 2016: customers were roasting at home
• the thermostat mysteriously began setting the
temperature to 90 degrees F (32 C)
• Hive:
‒ “We are aware of a temporary glitch…where a certain
sequence of commands in the Hive iOS app can cause
thermostat temperature to rise to 90 degrees F.”
4
Why test?
• Hive user:
5
Purpose of testing
• Every piece of software is written with some
functionality in mind
6
Testing and test cases
7
Approaches to testing
Black-box testing White-box testing
• Focuses only on • Focuses on the code
functionality ‒ examines the code to
‒ does not look at how figure out what tests to
the code actually works use
8
black-box testing
9
Black-box testing: what to test?
• Based purely on the desired functionality
‒ shouldn’t be influenced by the particular code you wrote
(that’s white-box testing)
• Aspects to consider:
‒ expected outcome
o normal vs error
‒ characterizing values
o edge cases vs “regular” values
10
Black-box testing: Outcomes
• Choose tests for both normal and error behaviors
‒ assumes that we know what the error situations are
• Passing a test:
‒ the program passes a test if it shows the desired
behavior for that test
11
Black-box testing: Values
• Edge cases:
‒ at or near the end(s) of the range of a value the program
is supposed to operate on
‒ Examples:
o “zero-related” : 0, [ ], empty string, empty file, ...
o “one-related” : 1, ‒1, list with one element, file with one line,
...
o (maybe) large values
• “Regular” values:
‒ not edge cases
12
Example:
“Read a file containing integers and print the sum of
the numbers that occur on odd-numbered lines.”
13
Example “Read a file containing integers and print the sum of
the numbers that occur on odd-numbered lines.”
15
Example “Read a file containing integers and print the sum of
the numbers that occur on odd-numbered lines.”
edge
• empty file • input file does not exist (or
is unreadable)
• file with one number
• file has non-numeric
• a file with several characters
regular numbers, one per line
• empty line
• more than one number on
a line
16
EXERCISE/ICA27
Do problems 1 and 2.
17
EXERCISE-ICA27-p.1
Consider this program specification:
Write a program that reads a file name and computes (and prints
out) the length of the longest line in that file.
21
EXERCISE-sol
Consider the rhyming words assignment.
Specify input files that exemplify each of the following:
24
White-box testing: what to test?
• Ideally, that every path through the code works
correctly
‒ but this can be prohibitively difficult and expensive
unit testing
• Instead, what we often do is:
‒ check that the individual pieces of the program work
properly
‒ verify expected inputs/outputs of individual functions
‒ use assert statements to check those assumptions (called
invariants)
25
Invariants and assertions
• Invariant: an expression at a program point that
always evaluates to True when execution reaches
that point
26
Invariants and assertions
• Assertion: a statement that some expression E is an
invariant at some point in a program
o Python syntax:
assert E
assert E, ″error message″
• assert:
o E evaluates to True or False
o If E evaluates to True, program execution continues
o otherwise, the error message is printed and execution halts with
an AssertionError
27
Invariants and assertions
28
Using asserts
• checking arguments to functions
‒ e.g., if an argument's value has to be positive
‒ Precondition(s) of the function
• checking data structure invariants
‒ e.g., i >= 0 and i < len(name)
• checking "can't happen" situations
‒ this also serves as documentation that the situation
can't happen
• after calling a function, to make sure its return
value is reasonable
‒ Postcondition(s) of the function
29
Using asserts
• Some invariants are complex:
‒ numlist has at least one even number
‒ arglist consists of strings that contain at least one
vowel
• You can write your own functions that can be used
in assert statements
30
EXERCISE/ICA27
Do problems 3, 4, and 5.
31
Whiteboard -REVIEW
• In black-box testing, what does the tester know about the code
being tested?
______________________________
o _________
o _________
o _________
__________________________________________
32
Unit testing
• Tests individual units of code, e.g., functions,
methods, or classes
‒ e.g.: given specific test inputs, does the function behave
correctly?
o CloudCoder!
‒ useful for making programmers focus on the exact
behavior of the function being tested
o e.g., preconditions, postconditions, invariants
‒ helps find problems early
• Isolate a unit and validate its correctness
• Often automated, but can be done manually
33
Unit testing
# grid_is_square(arglist) – returns True if arglist
# has the shape of a square grid, i.e.,
# the length of each element ("row") of arglist is
# equal to the number of rows of arglist
def grid_is_square(arglist):
num_rows = len(arglist)
for row in arglist:
if len(row) != num_rows:
return False
return True
34
Unit testing
# grid_is_square(arglist) – returns True if arglist
# has the shape of a square grid, i.e.,
# the length of each element ("row") of arglist is
# equal to the number of rows of arglist
def grid_is_square(arglist):
num_rows = len(arglist)
for row in arglist:
if len(row) != num_rows:
return False
return True • Write three white box test cases (inputs) for
this. (Use a whiteboard.)
• (I.e., give the specific arglist that would be
passed in to test the function.)
35
Code coverage
• Code coverage refers to how much of the code is
executed ("covered") by a set of tests
‒ want to be at (or close to) 100%
‒ coverage tools report which parts of the program were
executed, and how much
o e.g., Coberta.py, CodeCover, Coverage.py (Python), etc.
• Figuring out how to increase coverage often leads
to testing edge cases
36
Unit testing: practical heuristics
• Check both normal and error behaviors
• edge-case inputs:
‒ zero values (0, empty list/string/tuple/file, …)
‒ singleton values (1, list/string/tuple/file of length 1, …)
‒ large values
• if statements: make sure each outcome (True/False)
is taken
• Loops: test 0, 1, >1 iterations
37
Unit testing: what to check?
• Not just “output is what we expect”
‒ very often, this is the only thing that programmers
check
‒ not enough:
o a program can produce the expected output “accidentally”
38
Passing test cases "accidentally"
• Problem spec:
‒ "Write a function grid_is_square(arglist) that returns
True if arglist is a square grid, i.e., its no. of rows equals
its no. of columns."
39
Unit testing: what to check?
• Not just “output is what we expect”
‒ very often, this is the only thing that programmers
check
‒ not enough:
o a program can produce the expected output “accidentally”
40
Unit testing: what to check?
• Not just “output is what we expect”
‒ remember “accidental” success
• Check that invariants hold at key points
if
CHECK CHECK
CHECK
41
Unit testing: what to check?
• Check that invariants hold at key points
loop
CHECK
CHECK
42
Unit testing: what to check?
• Check that invariants hold at key points
①Check that nothing breaks if the loop
does not execute at all
loop
CHECK
CHECK
43
Unit testing: what to check?
• Check that invariants hold at key points
①Check that nothing breaks if the loop
does not execute at all
CHECK
44
Unit testing: what to check?
• Check that invariants hold at key points
①Check that nothing breaks if the loop
does not execute at all
45
Unit testing: summary
• Test normal (include edge cases) and error values
• If statements: test all branches (if/elif/else)
• Functions:
‒ check return values
46
Example: buggy list-lookup
# lookup(string, lst) -- returns the
# position where the given string
# occurs in lst.
def lookup(string, lst):
for i in range(len(lst)):
if string == lst[i]:
return i
47
Example: buggy list-lookup
# lookup(string, lst) -- returns the
# position where the given string
# occurs in lst. 0, 1, >1 iterations Þ lists
of length 0, 1, 2
def lookup(string, lst):
for i in range(len(lst)):
if string == lst[i]:
return i
48
Example: (buggy) list-lookup
# lookup(string, lst) -- returns the
# position where the given string
# occurs in lst. 0, 1, >1 iterations Þ lists
of length 0, 1, 2
def lookup(string, lst): both branches taken Þ
for i in range(len(lst)): string is at positions 0, 1
if string == lst[i]:
return i
49
Example: (buggy) list-lookup
# lookup(string, lst) -- returns the
# position where the given string
# occurs in lst. 0, 1, >1 iterations Þ lists
of length 0, 1, 2
def lookup(string, lst): both branches taken Þ
for i in range(len(lst)): string is at positions 0, 1
if string == lst[i]: some possible test inputs:
return i 'a', []
'a', ['a’]
'a', ['b','a’]
50
Example: (buggy) list-lookup
# lookup(string, lst) -- returns the
# position where the given string
# occurs in lst. 0, 1, >1 iterations Þ lists
of length 0, 1, 2
def lookup(string, lst): both branches taken Þ
for i in range(len(lst)): string is at positions 0, 1
if string == lst[i]: some possible test inputs:
return i 'a', []
Note: this will 'a', ['a’]
catch the no- 'a', ['b','a’]
return-value bug
51
EXERCISE/ICA28
Do problems 1 and 2.
52
EXERCISE-ICA28-p1
Write four unit tests for the function below:
53
EXERCISE-ICA28-p1
# average(lst) -- returns the
# average of the numbers in lst.
0, 1, >1 iterations Þ lists
def average(lst): of length 0, 1, 2
sum = 0
for i in range(len(lst)):
sum += lst[i]
return sum/len(lst)
54
EXERCISE-ICA28-p1
# average(lst) -- returns the
# average of the numbers in lst.
0, 1, >1 iterations Þ lists
def average(lst): of length 0, 1, 2
sum = 0
some possible test inputs:
for i in range(len(lst)): []
sum += lst[i] [17]
[5, 12]
return sum/len(lst)
55
EXERCISE-ICA28-p1
Write four unit tests for the function below:
return sum/len(lst)
Note: this will catch the
divide-by-zero on empty list
bug
56
EXERCISE-ICA28-p2
Write four unit tests for the function below:
57
Testing strategy
• Test as a part of program development
‒ try out small tests even when the code is only
partially developed (i.e., lots of stubs)
o helps catch problems at function boundaries, e.g., number
and types of arguments
o can help identify bugs in the design, e.g., missing pieces
58
EXERCISE/ICA28
Do problems 3 and 4.
59