Home > Articles

  • Print
  • + Share This
Like this article? We recommend

Testing and Debugging

I'm a great advocate of automated testing, and a library is the easiest kind of software in the world to test. (The hardest are things such as GUIs, operating systems, real-time apps, and threaded apps.)

It's immaterial to me whether you are an Extreme Programming (XP) junkie or not. There is much to be said for Test-Driven Design (TDD), in which the test case is written even before the code is written. But you don't have to go to such extremes. Just make sure that you have an adequate set of tests, that they are fully automated, and that you run them early and often.

Test every constructor and every method in all its signatures. Try to hit every program path; that is, every branch of an if or switch statement. Test every error that you can simulate, including where possible such conditions as "out of memory" and "out of disk space." Try to generate every exception that your program can throw.

Some things in the outside environment are hard to simulate, especially if you are performing web or network-related operations. Consider a mock objects approach in which an object (of your own making) simulates an outside entity such as a web server.

Do stress testing if it's appropriate for your application. This should be kept separate from unit testing.

If you don't have test driver software, consider an open source solution. CppUnit is a good one.

While you run the test suite, you can run other tools against the code. The profiler, as we mentioned before, is a useful tool if you're concerned about efficiency.

A code coverage tool is useful. It will check to see whether all the paths of your code are being exercised, and will often uncover bugs. At the very least, it will point out where your test cases are weak. Check whether your compiler comes with a code coverage analysis tool.

There are many other tools out there that test the integrity or "robustness" of your software. Many of these will detect memory violations, such as dangling pointers, which cause intermittent errors. The open source tool valgrind is especially good at finding memory leaks.

You should consider putting some kind of debugging interface into your library. It might be publicly documented for the benefit of the library user, or it might be for your own use only.

You might include methods that perform tracing of major functions. You might have methods that dump data structures or variable values to standard output or to a disk file.

None of these techniques rely on a symbol table, so you can still use them with a stripped binary. Ideally, the debugging techniques should be usable without recompiling; some ways to turn the features on and off would be API calls, command-line options, a standard environment variable, and so on.

It's up to you whether to leave these features in when you ship. If you are in a position of doing remote tech support, you might want to leave them in for your own benefit. In any case, if they are stripped out, it should be a matter of a makefile parameter to build them back in. Don't make it difficult.

You should at least consider publishing the debugging API. It may prove to be of benefit to your users as well as to you.

And speaking of publishing: We come now to the subject of documentation.

  • + Share This
  • 🔖 Save To Your Account