chapter 26 testing bjarne stroustrup

25
Chapter 26 Chapter 26 Testing Testing Bjarne Stroustrup Bjarne Stroustrup www.stroustrup.com/Programming www.stroustrup.com/Programming

Upload: nathaniel-jensen

Post on 26-Mar-2015

252 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: Chapter 26 Testing Bjarne Stroustrup

Chapter 26Chapter 26TestingTesting

Bjarne StroustrupBjarne Stroustrupwww.stroustrup.com/Programmingwww.stroustrup.com/Programming

Page 2: Chapter 26 Testing Bjarne Stroustrup

AbstractAbstract This lecture is an introduction to the design and This lecture is an introduction to the design and

testing of program units (such as functions and testing of program units (such as functions and classes) for correctness. We discuss the use of classes) for correctness. We discuss the use of interfaces and the selection of tests to run against interfaces and the selection of tests to run against them. We emphasize the importance of designing them. We emphasize the importance of designing systems to simplify testing and testing from the start. systems to simplify testing and testing from the start. Proving programs correct and performance problems Proving programs correct and performance problems are also briefly considered.are also briefly considered.

22Stroustrup/ProgrammingStroustrup/Programming

Page 3: Chapter 26 Testing Bjarne Stroustrup

OverviewOverview Correctness, proofs, and testingCorrectness, proofs, and testing DependenciesDependencies System testsSystem tests Testing GUIsTesting GUIs Resource managementResource management Unit and system testsUnit and system tests Finding assumptions that do not holdFinding assumptions that do not hold Design for testingDesign for testing PerformancePerformance

33Stroustrup/ProgrammingStroustrup/Programming

Page 4: Chapter 26 Testing Bjarne Stroustrup

CorrectnessCorrectness

Questions to ask about a programQuestions to ask about a program Is your program correct? Is your program correct? What makes you think so? What makes you think so? How sure are you?How sure are you? Why?Why? Would you fly in a plane that depended on that code?Would you fly in a plane that depended on that code?

You have to be able to reason about your code to have any real You have to be able to reason about your code to have any real certaintycertainty Programming is generally unsystematicProgramming is generally unsystematic Debugging is generally unsystematicDebugging is generally unsystematic What are you willing to bet that you found the last bug?What are you willing to bet that you found the last bug?

Related interesting questionsRelated interesting questions Could the program run forever if the hardware didn’t fail?Could the program run forever if the hardware didn’t fail? Does it always deliver its results in a reasonable time?Does it always deliver its results in a reasonable time?

44Stroustrup/ProgrammingStroustrup/Programming

Page 5: Chapter 26 Testing Bjarne Stroustrup

ProofsProofs

So why not just prove mathematically that our program is So why not just prove mathematically that our program is correct?correct? It’s often too hard and/or takes too longIt’s often too hard and/or takes too long Sometimes proofs are wrong too (even proofs produced by computers or Sometimes proofs are wrong too (even proofs produced by computers or

by experts!).by experts!). Computer arithmetic isn’t the same as “real” math—remember the Computer arithmetic isn’t the same as “real” math—remember the

rounding and overflow errors we saw (due to finite and limited rounding and overflow errors we saw (due to finite and limited precision)?precision)?

So we do what we can: follow good design principles, test, test, and then So we do what we can: follow good design principles, test, test, and then test some more!test some more!

55Stroustrup/ProgrammingStroustrup/Programming

Page 6: Chapter 26 Testing Bjarne Stroustrup

TestingTesting ““A systematic way to search for errors”A systematic way to search for errors” Real testers use a lot of toolsReal testers use a lot of tools

Unit test frameworksUnit test frameworks Static code analysis toolsStatic code analysis tools Fault injection toolsFault injection tools ……

When done well, testing is a highly skilled and most valuable When done well, testing is a highly skilled and most valuable activityactivity

““Test early and often”Test early and often” Whenever you write a function or a class, think of how you might test itWhenever you write a function or a class, think of how you might test it Whenever you make a significant change, re-testWhenever you make a significant change, re-test Before you ship (even after the most minor change), re-testBefore you ship (even after the most minor change), re-test

66Stroustrup/ProgrammingStroustrup/Programming

Page 7: Chapter 26 Testing Bjarne Stroustrup

TestingTesting

Some useful sets of values to check (especially boundary cases):Some useful sets of values to check (especially boundary cases): the empty setthe empty set small setssmall sets large setslarge sets sets with extreme distributionssets with extreme distributions sets where “what is of interest” happens near the endssets where “what is of interest” happens near the ends sets with duplicate elementssets with duplicate elements sets with even and with odd number of elementssets with even and with odd number of elements some sets generated using random numberssome sets generated using random numbers

77Stroustrup/ProgrammingStroustrup/Programming

Page 8: Chapter 26 Testing Bjarne Stroustrup

Primitive test harness for binary_search()Primitive test harness for binary_search()

int a1[ ] = { 1,2,3,5,8,13,21 };int a1[ ] = { 1,2,3,5,8,13,21 };if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),1) == false)if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),1) == false)

cout << "1 failed";cout << "1 failed";if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),5) == false) if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),5) == false)

cout << "2 failed";cout << "2 failed";if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),8) == false)if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),8) == false)

cout << "3 failed";cout << "3 failed";if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),21) == false)if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),21) == false)

cout << "4 failed";cout << "4 failed";if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),-7) == true) if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),-7) == true)

cout << "5 failed";cout << "5 failed";if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),4) == true) if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),4) == true)

cout << "6 failed";cout << "6 failed";if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),22) == true)if (binary_search(a1,a1+sizeof(a1)/sizeof(*a1),22) == true)

cout << "7 failed";cout << "7 failed";

88Stroustrup/ProgrammingStroustrup/Programming

Page 9: Chapter 26 Testing Bjarne Stroustrup

A Better Test Harness A Better Test Harness (stil primitive)(stil primitive)

Put the variables into a data file, Put the variables into a data file, e.g.e.g., with a format of, with a format of

{ 27 7 { 1 2 3 5 8 13 21} 0 }{ 27 7 { 1 2 3 5 8 13 21} 0 }

meaningmeaning

{test_number value {sequence} result}{test_number value {sequence} result}

i.e.i.e., test #, test #2727 calls our binary_search to look for the value calls our binary_search to look for the value 77 in the in the sequence sequence { 1 2 3 5 8 13 21}{ 1 2 3 5 8 13 21} and checks that the result is and checks that the result is 00 (false, that (false, that is, not found).is, not found).

Now it’s (relatively) easy to write lots of test cases, or even write another Now it’s (relatively) easy to write lots of test cases, or even write another program to generate a data file with lots of (random) cases.program to generate a data file with lots of (random) cases.

99Stroustrup/ProgrammingStroustrup/Programming

Page 10: Chapter 26 Testing Bjarne Stroustrup

DependenciesDependencies

Basically we want every function to:Basically we want every function to: have well-defined inputshave well-defined inputs have well-defined resultshave well-defined results

including any modifications to input parametersincluding any modifications to input parameters in a determinate amount of time (no infinite loops, please)in a determinate amount of time (no infinite loops, please)

not have dependencies on objects that are not its explicit inputsnot have dependencies on objects that are not its explicit inputs Hard to achieve in real lifeHard to achieve in real life

not use more resources than are available and appropriatenot use more resources than are available and appropriate E.g., time, memory, internet bandwidth, files, and locksE.g., time, memory, internet bandwidth, files, and locks

1010Stroustrup/ProgrammingStroustrup/Programming

Page 11: Chapter 26 Testing Bjarne Stroustrup

DependenciesDependencies

How many dependencies can you spot in this nonsense function?How many dependencies can you spot in this nonsense function?

int do_dependent(int a, int& b) // int do_dependent(int a, int& b) // messy functionmessy function // // undisciplined dependenciesundisciplined dependencies{{

int val ;int val ;cin>>val;cin>>val;vec[val] += 10;vec[val] += 10;cout << a;cout << a;b++;b++;return b;return b;

}}

1111Stroustrup/ProgrammingStroustrup/Programming

Page 12: Chapter 26 Testing Bjarne Stroustrup

Resource ManagementResource Management

What resources (memory, files, etc.) acquired may not always be What resources (memory, files, etc.) acquired may not always be properly released in this nonsense function?properly released in this nonsense function?

void do_resources1(int a, int b, const char* s) // void do_resources1(int a, int b, const char* s) // messy functionmessy function // // undisciplined resource useundisciplined resource use{{

FILE* f = fopen(s,"r"); FILE* f = fopen(s,"r"); // // open file (C style)open file (C style)int* p = new int[a]; int* p = new int[a]; // // allocate some memoryallocate some memoryif (b<=0) throw Bad_arg(); if (b<=0) throw Bad_arg(); // // maybe throw an exceptionmaybe throw an exceptionint* q = new int[b]; int* q = new int[b]; // // allocate some more memoryallocate some more memorydelete[ ] p; delete[ ] p; // // deallocate the memory pointed to by pdeallocate the memory pointed to by p

}}

1212Stroustrup/ProgrammingStroustrup/Programming

Page 13: Chapter 26 Testing Bjarne Stroustrup

Better Resource ManagementBetter Resource Management

// // less messy functionless messy functionvoid do_resources2(int a, int b, const string& s) void do_resources2(int a, int b, const string& s) {{

istream is(s.c_str(),"r"); istream is(s.c_str(),"r"); // // open fileopen filevector<int>v1(a); vector<int>v1(a); // // create vector (owning memory)create vector (owning memory)if (b<=0) throw Bad_arg(); if (b<=0) throw Bad_arg(); //// maybe throw an exceptionmaybe throw an exceptionvector<int> v2(b); vector<int> v2(b); // // create another vector (owning memory)create another vector (owning memory)

}}

Can do_resources2() leak anything?Can do_resources2() leak anything?

1313Stroustrup/ProgrammingStroustrup/Programming

Page 14: Chapter 26 Testing Bjarne Stroustrup

LoopsLoops

Most errors occur at the ends, Most errors occur at the ends, i.e.i.e., at the first case or the , at the first case or the last case. Can you spot 3 problems in this code? 4? 5?last case. Can you spot 3 problems in this code? 4? 5?

int do_loop(vector<int>& vec) // int do_loop(vector<int>& vec) // messy functionmessy function // // undisciplined loopundisciplined loop{{

int i;int i;int sum;int sum;while(i<=vec.size()) sum+=v[i];while(i<=vec.size()) sum+=v[i];return sum;return sum;

}}

1414Stroustrup/ProgrammingStroustrup/Programming

Page 15: Chapter 26 Testing Bjarne Stroustrup

Buffer OverflowBuffer Overflow

Really a special type of loop error, Really a special type of loop error, e.g.e.g., “storing more bytes than , “storing more bytes than will fit” into an array—where will fit” into an array—where dodo the “extra bytes” go? the “extra bytes” go? (probably not a good place)(probably not a good place)

The premiere tool of virus writers and “crackers” (evil hackers)The premiere tool of virus writers and “crackers” (evil hackers) Some vulnerable functions (best avoided):Some vulnerable functions (best avoided):

gets, scanfgets, scanf // // these are the worst: avoid!these are the worst: avoid! sprintfsprintf strcatstrcat strcpystrcpy ……

1515Stroustrup/ProgrammingStroustrup/Programming

Page 16: Chapter 26 Testing Bjarne Stroustrup

Buffer overflowBuffer overflow Don’t avoid unsafe functions just as a fetishDon’t avoid unsafe functions just as a fetish

Understand what can go wrong and don’t just write equivalent codeUnderstand what can go wrong and don’t just write equivalent code Even unsafe functions (e.g. Even unsafe functions (e.g. strcpy()strcpy()) have uses ) have uses

if you really want to copy a zero terminated string, you can’t do better than if you really want to copy a zero terminated string, you can’t do better than strcpy() strcpy() – just be – just be sure about your “strings” (How?)sure about your “strings” (How?)

char buf[MAX];char buf[MAX];

char* read_line()char* read_line() // // harmless? Mostly harmless? Avoid like the plague?harmless? Mostly harmless? Avoid like the plague?

{{

int i = 0;int i = 0;

char ch;char ch;

while (cin.get(ch) && ch!='\n') buf(i++)=ch;while (cin.get(ch) && ch!='\n') buf(i++)=ch;

buf[i+1]=0;buf[i+1]=0;

return buf;return buf;

}}

Stroustrup/ProgrammingStroustrup/Programming 1616

Page 17: Chapter 26 Testing Bjarne Stroustrup

Buffer overflowBuffer overflow Don’t avoid unsafe functions just as a fetishDon’t avoid unsafe functions just as a fetish

Understand what can go wrong and don’t just write equivalent codeUnderstand what can go wrong and don’t just write equivalent code Write simple and safe codeWrite simple and safe code

string buf;string buf;

getline(cin,buf); getline(cin,buf); // // buf expands to hold the newline terminated inputbuf expands to hold the newline terminated input

Stroustrup/ProgrammingStroustrup/Programming 1717

Page 18: Chapter 26 Testing Bjarne Stroustrup

BranchingBranching

In In ifif and and switchswitch statements statements Are all alternatives covered?Are all alternatives covered? Are the right actions associated with the right conditions?Are the right actions associated with the right conditions?

Be careful with nested Be careful with nested if if andand switch switch statementsstatements The compiler ignores your indentation The compiler ignores your indentation Each time you nest you must deal with all possible alternativesEach time you nest you must deal with all possible alternatives

Each level multiplies the number of alternatives (not just add)Each level multiplies the number of alternatives (not just add) For For switchswitch statements statements

remember the remember the default default case and to case and to breakbreak after each other case after each other case unless you really meant to “fall through” unless you really meant to “fall through”

1818Stroustrup/ProgrammingStroustrup/Programming

Page 19: Chapter 26 Testing Bjarne Stroustrup

System TestsSystem Tests

Do unit tests first, then combinations of units, and so on, till we Do unit tests first, then combinations of units, and so on, till we get to the whole systemget to the whole system Ideally, in isolation from other parts of the systemIdeally, in isolation from other parts of the system Ideally, in a repeatable fashionIdeally, in a repeatable fashion

What about testing GUI based applications?What about testing GUI based applications? Control inversion makes GUI testing difficultControl inversion makes GUI testing difficult Human behavior is not exactly repeatable Human behavior is not exactly repeatable

Timing, forgetfulness, boredom, etc.Timing, forgetfulness, boredom, etc. Humans still needed at some point (only a human can evaluate “look and feel”)Humans still needed at some point (only a human can evaluate “look and feel”)

Simulate user input from a test scriptSimulate user input from a test script That way a test harness script takes the place of the human for many testsThat way a test harness script takes the place of the human for many tests

An excellent application of “layering” with well-defined interfaces An excellent application of “layering” with well-defined interfaces between the layersbetween the layers

Allows for portability of applications across GUI systemsAllows for portability of applications across GUI systems A GUI is often used as a lock-in mechanismA GUI is often used as a lock-in mechanism

1919Stroustrup/ProgrammingStroustrup/Programming

Page 20: Chapter 26 Testing Bjarne Stroustrup

Testing ClassesTesting Classes A type of unit testA type of unit test

but most class objects have state but most class objects have state Classes often depend on interactions among member functionsClasses often depend on interactions among member functions

A base class must be tested in combination with its derived classesA base class must be tested in combination with its derived classes Virtual functionsVirtual functions Construction/initialization is the combined responsibility of several classesConstruction/initialization is the combined responsibility of several classes Private data is really useful here (beware of protected data members)Private data is really useful here (beware of protected data members)

Take our Take our Shape Shape class as an example:class as an example: Shape Shape has several functionshas several functions A A Shape Shape has a mutable state (we can add points, change color, etc.); that is, has a mutable state (we can add points, change color, etc.); that is,

the effect of one function can affect the behavior of another functionthe effect of one function can affect the behavior of another function Shape Shape has has virtual virtual functions; that is, the behavior of a functions; that is, the behavior of a Shape Shape depends on depends on

what (if any) class has been derived from itwhat (if any) class has been derived from it Shape Shape is not an algorithm (why not?)is not an algorithm (why not?) A change to a A change to a Shape Shape can have an effect on the screen (so maybe we still can have an effect on the screen (so maybe we still

need a human tester?)need a human tester?)

2020Stroustrup/ProgrammingStroustrup/Programming

Page 21: Chapter 26 Testing Bjarne Stroustrup

Finding assumptions that do not holdFinding assumptions that do not hold For example, illegal input argumentsFor example, illegal input arguments

Should never happen, but it doesShould never happen, but it does Check before each call or at the beginning of the functionCheck before each call or at the beginning of the function

Depending on which code we can modifyDepending on which code we can modify E.g.E.g., , sqrtsqrt first checks that its argument is a non-negative value first checks that its argument is a non-negative value

That can be difficult/problematic:That can be difficult/problematic: Consider Consider binary_search(a,b,v); binary_search(a,b,v); // is v in [a:b)// is v in [a:b)

For forward iterators (For forward iterators (e.g.e.g., for a , for a listlist), we can’t test if ), we can’t test if a<ba<b – no < operation – no < operation For random-access iterators ,we can’t check if a and b are part of the same For random-access iterators ,we can’t check if a and b are part of the same

sequencesequence The only perfect solution involves a run-time checking libraryThe only perfect solution involves a run-time checking library

Scanning the entire sequence to verify it’s sorted is much more work than Scanning the entire sequence to verify it’s sorted is much more work than actually doing the binary searchactually doing the binary search

The purpose of binary_search() is to be faster than linear searchThe purpose of binary_search() is to be faster than linear search Sometimes, check in “debug/test mode” onlySometimes, check in “debug/test mode” only

Leave (only) affordable tests in production codeLeave (only) affordable tests in production code

2121Stroustrup/ProgrammingStroustrup/Programming

Page 22: Chapter 26 Testing Bjarne Stroustrup

Design for TestingDesign for Testing

Use well-defined interfacesUse well-defined interfaces so that you can write tests for the use of these interfacesso that you can write tests for the use of these interfaces Define invariants, pre- and post conditionsDefine invariants, pre- and post conditions

Have a way of representing operations as textHave a way of representing operations as text so that they can be stored, analyzed and replayedso that they can be stored, analyzed and replayed

Embed tests of unchecked assumptions (assertions) in the Embed tests of unchecked assumptions (assertions) in the calling and/or called codecalling and/or called code to catch bad arguments before system testingto catch bad arguments before system testing

Minimize dependencies and keep dependencies explicitMinimize dependencies and keep dependencies explicit To make it easier to reason about the codeTo make it easier to reason about the code

Have a clear resource management strategyHave a clear resource management strategy

This will also minimize debugging!This will also minimize debugging!

2222Stroustrup/ProgrammingStroustrup/Programming

Page 23: Chapter 26 Testing Bjarne Stroustrup

PerformancePerformance

Is it efficient enough? Is it efficient enough? Note: Note: NotNot “Is it as efficient as possible?” “Is it as efficient as possible?” Computers are fast: You’ll have to do millions of operations Computers are fast: You’ll have to do millions of operations

to even notice (without using tools)to even notice (without using tools) Accessing permanent data (on disc) repeatedly can be Accessing permanent data (on disc) repeatedly can be

noticednoticed Accessing the web repeatedly can be noticed Accessing the web repeatedly can be noticed

Time “interesting” test casesTime “interesting” test cases e.g.e.g., using , using time time oror clock() clock() Repeat ≥3 times; should be ± 10% to be believableRepeat ≥3 times; should be ± 10% to be believable

2323Stroustrup/ProgrammingStroustrup/Programming

Page 24: Chapter 26 Testing Bjarne Stroustrup

PerformancePerformance What’s wrong with this?What’s wrong with this?

for (int i=0; i<strlen(s); ++i) {for (int i=0; i<strlen(s); ++i) {

// // do something with s[i]do something with s[i]

}}

It was part of an internet message log analyzerIt was part of an internet message log analyzer Used for files with many thousands of long log linesUsed for files with many thousands of long log lines

Stroustrup/ProgrammingStroustrup/Programming 2424

Page 25: Chapter 26 Testing Bjarne Stroustrup

Using Using clock()clock()

int n = 10000000; int n = 10000000; // // repeat do_somenting() n timesrepeat do_somenting() n timesclock_t t1 = clock();clock_t t1 = clock();if (t1 = = clock_t(-1)) { if (t1 = = clock_t(-1)) { // // clock_t(-1) means “clock() didn't work”clock_t(-1) means “clock() didn't work”

cerr << "sorry, no clock\n";cerr << "sorry, no clock\n";exit(1);exit(1);

}}for (int i = 0; i<n; i++) do_something(); for (int i = 0; i<n; i++) do_something(); // // timing looptiming loopclock_t t2 = clock();clock_t t2 = clock();if (t2 = = clock_t(-1)) {if (t2 = = clock_t(-1)) {

cerr << "sorry, clock overflow\n";cerr << "sorry, clock overflow\n";exit(2);exit(2);

}}cout << "do_something() " << n << " times took "cout << "do_something() " << n << " times took "

<< double(t2-t1)/CLOCKS_PER_SEC << " seconds " << double(t2-t1)/CLOCKS_PER_SEC << " seconds " // // scale resultscale result<< " (measurement granularity: "<< " (measurement granularity: "<< CLOCKS_PER_SEC << " of a second)\n";<< CLOCKS_PER_SEC << " of a second)\n";

2525Stroustrup/ProgrammingStroustrup/Programming