A while ago, in the truisms series I talked about how if you were not testing your software regularly and consistently then you couldn't have confidence that it works. At the time, I didn't go into a lot of detail about ways to do software testing; I talked about some types of testing but not a lot and I left a large amount of it to you to research and learn.
Today I'd like to take some time to go into a little bit more detail about mechanisms and techniques for software testing, and show that just because modern techniques seem to focus on managed or scripting languages doesn't mean that they can't apply to C or similar. Wikipedia has a very good set of articles about software testing and I recommend you read them, but before you do, let me hilight a few things you might want to consider, based on my experience with testing C/C++, Lua, Python, Haskell and shell programs.
The first thing I'd like to hilight is that any testing that you do must be a live part of your project. Tests are not write-once-worry-never code. They should evolve with your project and for the most part, test code ought to be the very highest quality code in your project. If your tests are bad, they might be giving you false confidence (or false lack of confidence) in your project resulting in either undiscovered bugs, or wasted effort.
Tests should be simple, they should be clear to people who don't necessarily know the codebase intimately, they should be well documented, and perhaps most importantly of all, they should be obviously correct. This is a tall order for any code, and for test code it can be even harder, but if you strive for these attributes in your test code, you'll appreciate your tests more and more over time.
The second point I'd like to labour is that there is no such thing as a right or wrong testing technique. Different techniques are good at testing different kinds of surface and exposing different kinds of bugs or issues. Don't be reticent to add different kinds of tests to your codebase, and also don't be afraid to test the same thing in multiple different ways.
The third point links from the second -- test the surfaces you expose for the
features you support. Different surfaces lend themselves to different kinds of
tests, and so you will implicitly start to gather different test types. For
instance, your project might involve a library (internal or otherwise) which
supports a command line application. Testing internal surfaces of the library
where appropriate (e.g. simple purpose utility classes or equivalent) and
testing the external surface of the command line application are probably the
two places to best concentrate your efforts. If your library is also exposed
to others (e.g. if the commandline app is an example such as
apt-get is) then
you should also explicitly test the API you are claiming to support.
Testing techniques that I have benefitted from, and believe you will benefit strongly from learning about and applying in your projects are:
- Unit testing for library APIs and similar
- There's such a multitude of unit testing frameworks I'm not going to suggest a particular one.
- Interestingly though, this overlaps with scenario testing to some extent thanks to tools like cucumber.
- Scenario testing for higher level surfaces such as tools or services
- Here I will make a partisan recommendation for the yarn tool which I helped to design and Lars wrote.
- If you try no other method of testing, try this one. Yarn re-kindled my personal joy in testing software.
I have written my own test frameworks, I have used frameworks which exist already, and I cannot say which I prefer. Using existing frameworks certainly saves a lot of effort when you want features such as running each test in a subprocess, but sometimes nothing is as elegant as writing your own framework in 20 lines of Lua when you want it.
All in all, my strong recommendation is learn to test, and learn to love to test too. Now get on and read the Wikipedia software testing page and also Martin Fowler's article index on the matter and get busy.