Lecture Notes for CS 325
Functional and Structural Testing, 22 March 2000
- two main approaches to testing - functional or structural
- functional - testing based on what the system does
- structural - testing based on how the system works
- specifications determine the test cases and correct results
- functional testing
- black-box testing - some inputs, some outputs
- exhaustive test cases - through, but not optimal or usually even
practical
- random sampling - cheap but not effective
- equivalence-class test cases - partition exhaustive test cases into
equivalent sets of test cases
- some test cases should have the same result, so just using one should
work
- the spec determines which test cases should have the same result
- the obvious partitioning - valid and invalid results
- further conditions can further partition equivalence classes
- output equivalence classes too - combinations of possible outputs
- boundary values - values at the edges of equivalence classes
- output boundary values too
- cause and effect graphing - trying to fill in the black box
- causes characterize inputs, effects characterize outputs
- the spec establishes the connection between cause and effects
- the resulting graph serves as a driver for test case generation
- special cases and experience
- the spec defines special cases
- standard programming (mal) practice defines special cases - off by
one errors, accumulating garbage in c++ programs
- experience suggests others - unchecked text input
- structural testing
- white- (or clear- or glass-) box testing - inputs, outputs, and what's
between
- the effort is on testing what's in-between the inputs and outputs
- execute every statement in the module
- control-flow graph - all the paths from entrance to exit
- branch coverage - take each branch in each direction
- complex conditionals hide much information - require each condition
component to be true and false
- execute all paths from the entrance to the exit - loops are the problem
here
- use cyclomatic complexity to bound the loop iterations
- data-flow testing
- variable def-use - use in computation or predicate
- all-defs test - execution uses every variable value
- all-p-uses - execution uses every conditional variable value
- all-p-uses, some-c-uses
- all-uses
- mutation testing
- seeding errors and looking for them
- create program mutations by changing the program under test
- mutations may change operators, or add one to array indices
- classes of mutations
- mutation testing steps
- generate the test cases for P
- generate a set of mutants for P
- run the test cases over the mutant set
- each undetected mutant is live
- augment the test cases to detect the live mutants
- difficulties with mutation analysis
- finding live mutants equivalent to P
- augmenting the test set to detect live mutants
- determining what the improved test set means to P
- determining the mutant set
- running the mutation tests
- difficult but worth it
- support for structural testing
- automated support is a natural, but many problems are intractable or
uncomputable
- generating test data
- running the tests
- analyzing the test results
- characterizing the result quality
This page last modified on 24 March 2000.