1 / 23

Testing and Debugging

Testing and Debugging. Hakam Alomari halomari@cs.kent.edu. 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

mirit
Download Presentation

Testing and Debugging

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Testing and Debugging HakamAlomari halomari@cs.kent.edu

  2. 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

  3. 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_H Class string { //… }; #endif

  4. Example [wiki] File "grandfather.h“ structfoo { int member; }; File "father.h“ #include "grandfather.h" File "child.c“ #include "grandfather.h" #include "father.h"

  5. Use of #include guards File "grandfather.h“ #ifndef GRANDFATHER_H #define GRANDFATHER_H structfoo { int member; }; #endif File "father.h“ #include "grandfather.h" File "child.c“ #include "grandfather.h" #include "father.h"

  6. Namespaces • Used to group entities (e.g., objects) under a name, such as each group has its own name • Example: namespace foo1 { intvar = 1; } namespace foo2 { intvar = 2; } int main () { std::cout<< foo1::var <<“\n”; // output 1 std::cout<< foo2::var <<“\n”; // output 2 return 0; }

  7. 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"; // ...

  8. 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

  9. 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

  10. 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

  11. 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

  12. 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

  13. 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

  14. Building Test Cases • Determine the I/O spec. for the method • Develop test cases • Method implementation • Run the method against the test cases from 2 • Debugging (fix) • Go to 4

  15. 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);

  16. 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

  17. 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

  18. 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";

  19. 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

  20. 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

  21. 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

  22. Makefile Example # Create the executable is the first rule here # Link main: bigint.omain.o g++ -Wall bigint.omain.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

  23. 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/

More Related