I've started to dig into the GLib documentation and discovered that it also offers a unit testing framework.
But how could you do unit tests in a procedural language? Or does it require to program OO in C?
I've started to dig into the GLib documentation and discovered that it also offers a unit testing framework.
But how could you do unit tests in a procedural language? Or does it require to program OO in C?
At the most basic level, unit tests are just bits of code that execute other bits of code and tell you if they worked as expected.
You could simply make a new console app, with a main() function, that executed a series of test functions. Each test would call a function in your app and return a 0 for success or another value for failure.
I'd give you some example code, but I'm really rusty with C. I'm sure there are some frameworks out there that would make this a little easier too.
Just implement functions and exercise code. Exactly as David Hogue said.
The simplest way of doing a unit test is to build a simple driver code that gets linked with the other code, and call each function in each case...and assert the values of the results of the functions and build up bit by bit...that's how I do it anyway
int main(int argc, char **argv){ // call some function int x = foo(); assert(x > 1); // and so on.... }
Hope this helps, Best regards, Tom.
There is nothing intrinsically object-oriented about testing small pieces of code in isolation. In procedural languages you test functions and collections thereof.
If you are desperate, and you'd have to be desperate, I banged together a little C preprocessor and gmake based framework. It started as a toy, and never really grew up, but I have used it to develop and test a couple of medium sized (10,000+ line) projects.
Dave's Unit Test is minimally intrusive yet it can do some tests I had originally thought would not be possible for a preprocessor based framework (you can demand that a certain stretch of code throw a segmentation fault under certain conditions, and it will test it for you).
It is also an example of why making heavy use of the preprocessor is hard to do safely.
Unit testing only requires "cut-planes" or boundaries at which testing can be done. It is quite straightforward to test C functions which do not call other functions, or which call only other functions that are also tested. Some examples of this are functions which perform calculations or logic operations, and are functional in nature. Functional in the sense that the same input always results in the same output. Testing these functions can have a huge benefit, even though it is a small part of what is normally thought of as unit testing.
More sophisticated testing, such as the use of mocks or stubs is also possible, but it is not nearly as easy as it is in more dynamic languages, or even just object oriented languages such as C++. On way to approach this is to use #defines. One example of this is this article, Unit testing OpenGL applications, which shows how to mock out OpenGL calls. This allows you to test that valid sequences of OpenGL calls are made.
Another option is to take advantage of weak symbols. For example, all MPI API functions are weak symbols, so if you define the same symbol in your own application, your implementation overrides the weak implementation in the library. If the symbols in the library weren't weak, you would get duplicate symbol errors at link time. You can then implement what is effectively a mock of the entire MPI C API, which allows you to ensure that calls are matched up properly and that there aren't any extra calls that could cause deadlocks. It is also possible to load the library's weak symbols using dlopen()
and dlsym()
, and pass the call on if necessary. MPI actually provides the PMPI symbols, which are strong, so it is not necessary to use dlopen()
and friends.
You can realize many of the benefits of unit testing for C. It is slightly harder, and it may not be possible to get the same level of coverage you might expect from something written in Ruby or Java, but it's definitely worth doing.
With C it must go further than simply implementing a framework on top of existing code.
One thing I've always done is make a testing module (with a main) that you can run little tests from to test your code. This allows you to do very small increments between code and test cycles.
The bigger concern is writing your code to be testable. Focus on small, independent functions that do not rely on shared variables or state. Try writing in a "Functional" manner (without state), this will be easier to test. If you have a dependency that can't always be there or is slow (like a database), you may have to write an entire "mock" layer that can be substituted for your database during tests.
The principle unit testing goals still apply: ensure the code under test always resets to a given state, test constantly, etc...
When I wrote code in C (back before Windows) I had a batch file that would bring up an editor, then when I was done editing and exited, it would compile, link, execute tests and then bring up the editor with the build results, test results and the code in different windows. After my break (a minute to several hours depending on what was being compiled) I could just review results and go straight back to editing. I'm sure this process could be improved upon these days :)