Download pptx - Testing and Debugging

Transcript
Page 1: Testing and Debugging

Testing and Debugging

Hakam [email protected]

Page 2: Testing and Debugging

Outline

• Include Guards• Namespace name access• Testing Concepts– What is software testing?– How to find faults?– Black Box vs. Glass Box testing– How many tests cases are there?– Regression testing

• Using assert and cerr• Building test cases, Makefile

Page 3: Testing and Debugging

Include Guards

• Are used to prevent the contents of a file from being included more than once– Example: (string.h)

#ifndef CS2_STRING_H#define CS2_STRING_HClass string {

//…

};#endif

Page 4: Testing and Debugging

Example [wiki]

File "grandfather.h“struct foo {

int member;

}; File "father.h“

#include "grandfather.h" File "child.c“

#include "grandfather.h" #include "father.h"

Page 5: Testing and Debugging

Use of #include guardsFile "grandfather.h“

#ifndef GRANDFATHER_H #define GRANDFATHER_H struct foo {

int member;

}; #endif

File "father.h“#include "grandfather.h"

File "child.c“#include "grandfather.h" #include "father.h"

Page 6: Testing and Debugging

Namespaces• Used to group entities (e.g., objects) under a name, such as each group

has its own name• Example:

namespace foo1 {int var = 1;

}namespace foo2 {

int var = 2;

}

int main () {std::cout<< foo1::var <<“\n”; // output 1std::cout<< foo2::var <<“\n”; // output 2return 0;

}

Page 7: Testing and Debugging

Standard Includes

• Names defined by standard includes (e.g., cout) can be used as: – Prefix the name with std::– Use using std::name before the name is used– Put using namespace::std before the name is used– Example:

int main() { std::cout << "Hello World!\n"; // ...

int main() { using std::cout; cout << "Hello World!\n"; // ...

using namespace std; int main() { cout << "Hello World!\n"; // ...

Page 8: Testing and Debugging

Software Testing

• Software testing is used to find errors • error:– Incorrect output for a given input– Caused by faults inside the program– A fault is represent the incorrect part of the code• E.g., incorrect stopping condition

• So, test cases should be developed to exercise the program and uncovering errors

Page 9: Testing and Debugging

Testing levels

• Start by testing each method (unit tests)• Then each class in full (module tests)• Then the whole program (system tests)

• The test case that has a high probability to uncover an error is known to be the best one

Page 10: Testing and Debugging

Testing Types

• Black box vs Glass box (white box)– Black box• Uses only the I/O spec. to develop test cases

– Glass box • Uses only the implementation details to develop test

cases

• Both types are necessary to develop a good set of test cases

Page 11: Testing and Debugging

Number of Test Cases

• Functions usually have a very large number of pre. and post. conditions

• So, there is no need to test all of these to make sure our function behaves correctly

• How?– Pairing down test cases, by:– Develop equivalence classes of test cases– Examine the boundaries of these classes carefully

Page 12: Testing and Debugging

Equivalence Classes

• The input and output often fall into equivalence partitions where the system behaves in an equivalent way for each class

• Then, the test cases should be developed to test each partition

Page 13: Testing and Debugging

Classes Boundaries

• Example:– Partition system inputs and outputs into

equivalence classes– If input is a 2 digit integer between 10 and 99

then the equivalence partitions are < 10 (invalid), 10 – 99 (valid), and > 99 (invalid)

– Choose test cases at the boundary of these partitions

– 09, 10, 99, 100

Page 14: Testing and Debugging

Building Test Cases

1. Determine the I/O spec. for the method2. Develop test cases3. Method implementation 4. Run the method against the test cases

from 25. Debugging (fix)6. Go to 4

Page 15: Testing and Debugging

Test Steps• There are three steps in a test:

– Set-up the "fixture", the system to be tested– Perform the test– Verify the results, make sure there are no unwanted side effects

• Example: Test for existence of const char& operator[](int) const – // Setup fixture

• const string str("abc"); – // Test

• assert(str[0] == 'a'); • assert(str[1] == 'b'); • assert(str[2] == 'c');

– // Verify • assert(str.length() == 3);

Page 16: Testing and Debugging

Regression Testing

• The intent of regression testing is to ensure that a change, such as a bugfix, did not introduce new faults

• Each time you add a new method to your class or fix a fault run your test cases (all of them)– Adding something new or fixing a problem may

have side effects• Re-running your test cases will uncover these

problems

Page 17: Testing and Debugging

Asserts

• Used to output an error message and terminate a program if a condition is false

• Asserts are useful for testing• #include <cassert> is required to use asserts• Example:– assert(result == correct_result);

• Asserts can be turned off using– #define NDEBUG

Page 18: Testing and Debugging

Debugging• Used when the program is not working properly • One easy thing to do is output the value of relevant variables• Use cerr instead of cout. cout is buffered• Buffering means output is accumulated in a "buffer" and the

accumulated output sent out to the OS together• Output to cerr is not buffered, it is output immediately• If a program crashes there may be output in a buffer that gets

lost• It is a good idea to include a message with values that are

output– std::cerr << "var1 = " < < var1 << "\n";

Page 19: Testing and Debugging

The make Command

• The make command may be used to automate compiling

• The make command when executed will look for a file named makefile and then, if not found, for a file named Makefile to specify dependencies

• Otherwise a makefile must be specified with the -f option

• The make command has some rules built-in so it can do some basic compiling but usually the developer provides a makefile

Page 20: Testing and Debugging

Makefile Rules• Rules have the form:

– target: dependency_list – TAB command

• Target A file or name• dependency_list Files that the target "depends on"• TAB The TAB character. REQUIRED!• Command Typically a compiling/linking command

but can be any command. There may be more than one TAB/command line

• If the modification time of any dependency is more recent than the target the command lines are executed

Page 21: Testing and Debugging

Common Rule Types

• Creating machine language, .o, files and linking are two things that must be done# Link executable: file_1.o file_2.o

g++ -Wall file_1.o file_2.o -o executable

# Create .o file file.o: file.h file.cpp

g++ -Wall -c file.cpp

• Note .cpp files are typically not targets, .cpp files do not depend on other files

Page 22: Testing and Debugging

Makefile Example# Create the executable is the first rule here # Link main: bigint.o main.o

g++ -Wall bigint.o main.o -o main # Create .o file main.o: bigint.h main.cpp

g++ -Wall -c main.cpp # Create .o file bigint.o: bigint.h bigint.cpp

g++ -Wall -c bigint.cpp # Remove .o and executable clean:

rm -f *.o rm -f main

Page 23: Testing and Debugging

References

• [wiki]: http://en.wikipedia.org/wiki/Include_guard

• Computer Science II Lab: http://classes.cs.kent.edu/courses/cs33001_lab/svn/index.html

• Software Testing for CS II slides: http://www.cs.kent.edu/~jmaletic/CS33001/


Recommended