In my internship, one thing that is heavily stressed is to have 100% code coverage. This means that every single possible path you can go through in a function will be explored and made sure they are each successful. For example, if you have an if and an else statement and your program usually only goes through the if statement and not the else, you want to make sure the else statement is also tested.
I will be discussing the cmocka framework for testing with the C programming language, though I'm sure there are many other kinds. Cmocka is just the one I'm most familiar with.
There are two kinds of tests you can do. There is the unit test and the functional test. The functional test will call a function and if there is another function called inside of it, that function will also be called. This kind of testing is very simple to do: all you have to do is call the function you want to test and just make sure doing that doesn't throw any errors.
Unit testing, on the other hand, is a very powerful tool, but also a little more complicated to implement. Unit testing tests functions in isolation. If the function you want to test called another function inside of it, you don't want to call the actual function. You want to call something called a mock of the function.
Think of a mock as a test double. Instead of calling the actual function inside your function under test and risking something it does affecting the results of your function under test, you make a mock of the function inside your function under test and call that instead. The mock function just takes the place of the actual function.
You might be wondering how the program will know to call the mock if you just want to test everything or call the actual function if you want to actually run the program, then. The answer, at least for the C programming language and the cmocka framework, is that unit testing is done during the pre-processing stage. The pre-processing stage occurs before the actual processing is done. The C programming language using cmocka allows you to give commands for the pre-processing stage by using macros.
The macro is basically a pre-defined, all-caps string. The program goes through everything once over in the pre-processing stage. If this macro string is encountered, then this macro will be replaced with something different depending on if it's the pre-processing stage or the actual processing stage. This concept is used when unit testing.
Another thing you want to test as you're unit testing is to make sure that the parameters that are passed through are correct. This is done by having a stack. In this stack, you put in values that must be tested (in this case, your parameters), and on the other side, you will take values out of the stack one by one to test the values. To put values into this stack, you use a will_return function call. To take values out for testing, you use a mock() function call. To test your parameters, you can use assert functions, usually assert_int_equal, inside of your mock functions.
Inside of your assert function call, you usually have two parameters: one is a mock() call that takes the value from your stack to test, and the other is what that mock should actually be. Because will_return and mock() are intertwined in this manner, you can never have more or less mock() calls than will_return. If this occurs, you will get a noticeable error to indicate there's still stuff in your stack or you're trying to pull from an empty stack.
I hope this helps any new developers understand unit testing. Unit testing is very fundamental and is the best way to catch errors during programming. If a developer accidentally misspells a variable inside of a function that already has a unit test function attached to it, then that error will be caught during unit testing. Similarly, if a developer re-arranges variables or removed variables or alters parameter results, the unit test will catch that.
Top comments (0)