1
2# Cmockery Unit Testing Framework
3Cmockery is a lightweight library that is used to author C unit tests.
4
5## Motivation
6There are a variety of C unit testing frameworks available however many of them are fairly complex and require the latest compiler technology. Some development requires the use of old compilers which makes it difficult to use some unit testing frameworks. In addition many unit testing frameworks assume the code being tested is an application or module that is targeted to the same platform that will ultimately execute the test. Because of this assumption many frameworks require the inclusion of standard C library headers in the code module being tested which may collide with the custom or incomplete implementation of the C library utilized by the code under test.
7
8Cmockery only requires a test application is linked with the standard C library which minimizes conflicts with standard C library headers. Also, Cmockery tries avoid the use of some of the newer features of C compilers.
9
10This results in Cmockery being a relatively small library that can be used to test a variety of exotic code. If a developer wishes to simply test an application with the latest compiler then other unit testing frameworks maybe preferable.
11
12## Overview
13Cmockery tests are compiled into stand-alone executables and linked with the Cmockery library, the standard C library and module being tested. Any symbols external to the module being tested should be mocked - replaced with functions that return values determined by the test - within the test application. Even though significant differences may exist between the target execution environment of a code module and the environment used to test the code the unit testing is still valid since its goal is to test the logic of a code modules at a functional level and not necessarily all of its interactions with the target execution environment.
14
15It may not be possible to compile a module into a test application without some modification, therefore the preprocessor symbol UNIT_TESTING should be defined when Cmockery unit test applications are compiled so code within the module can be conditionally compiled for tests.
16
17## Installation
18
19### Binary Installation
20Cmockery can be installed on Fedora/EPEL systems by doing the following:
21
22```
23$ sudo yum install cmockery2 cmockery2-devel
24```
25
26### Installing from the source
27
28#### Environment Setup
29
30* Ubuntu
31
32```sh
33$ sudo apt-get -y install libtool make autoconf automake gcc
34```
35
36* Fedora/RHEL
37
38```sh
39$ sudo yum -y install libtool make autoconf automake gcc
40```
41
42* Mac (with Homebrew)
43```sh
44xcode-select --install # if we to install clang
45
46$ brew install autoconf automake libtool
47```
48
49#### Build cmockery
50
51```sh
52$ ./autogen.sh
53$ ./configure
54$ make
55```
56
57## Test Execution
58Cmockery unit test cases are functions with the signature void `function(void **state)`. Cmockery test applications initialize a table with test case function pointers using `unit_test*()` macros. This table is then passed to the `run_tests()` macro to execute the tests. `run_tests()` sets up the appropriate exception / signal handlers and other data structures prior to running each test function. When a unit test is complete `run_tests()` performs various checks to determine whether the test succeeded.
59
60The second parameter to `run_tests()` is the prefix to the xUnit XML report in the following format: *prefix*\_xunit.xml.  For example, calling `run_tests(tests, "abcd")` will create an xUnit XML report called `abcd_xunit.xml`.
61
62### Using run_tests()
63
64#### run\_test.c
65
66```c
67#include <stdarg.h>
68#include <stddef.h>
69#include <setjmp.h>
70#include <cmockery.h>
71
72// A test case that does nothing and succeeds.
73void null_test_success(void **state) {
74}
75
76int main(int argc, char* argv[]) {
77    const UnitTest tests[] = {
78        unit_test(null_test_success),
79    };
80    return run_tests(tests, "run");
81}
82```
83
84## Exception Handling
85Before a test function is executed by run_tests(), exception / signal handlers are overridden with a handler that simply displays an error and exits a test function if an exception occurs. If an exception occurs outside of a test function, for example in Cmockery itself, the application aborts execution and returns an error code.
86
87## Failure Conditions
88If a failure occurs during a test function that's executed via run_tests(), the test function is aborted and the application's execution resumes with the next test function. Test failures are ultimately signalled via the Cmockery function fail(). The following events will result in the Cmockery library signalling a test failure...
89
90## Assertions
91Runtime assert macros like the standard C library's assert() should be redefined in modules being tested to use Cmockery's mock_assert() function. Normally mock_assert() signals a test failure. If a function is called using the expect_assert_failure() macro, any calls to mock_assert() within the function will result in the execution of the test. If no calls to mock_assert() occur during the function called via expect_assert_failure() a test failure is signalled.  This is easily handled by including `cmockery_override.h`.
92
93### Using mock_assert()
94
95#### assert\_module.c
96
97```c
98#include <assert.h>
99
100// Override functions in the program to be tested
101#if UNIT_TESTING
102// Must be last include
103#include <cmockery/cmockery_override.h>
104#endif // UNIT_TESTING
105
106void increment_value(int * const value) {
107    assert(value);
108    (*value) ++;
109}
110
111void decrement_value(int * const value) {
112    if (value) {
113        *value --;
114    }
115}
116```
117
118### assert\_module\_test.c
119
120```c
121#include <stdarg.h>
122#include <stddef.h>
123#include <setjmp.h>
124#include <cmockery.h>
125
126extern void increment_value(int * const value);
127
128/* This test case will fail but the assert is caught by run_tests() and the
129 * next test is executed. */
130void increment_value_fail(void **state) {
131    increment_value(NULL);
132}
133
134// This test case succeeds since increment_value() asserts on the NULL pointer.
135void increment_value_assert(void **state) {
136    expect_assert_failure(increment_value(NULL));
137}
138
139/* This test case fails since decrement_value() doesn't assert on a NULL
140 * pointer. */
141void decrement_value_fail(void **state) {
142    expect_assert_failure(decrement_value(NULL));
143}
144
145int main(int argc, char *argv[]) {
146    const UnitTest tests[] = {
147        unit_test(increment_value_fail),
148        unit_test(increment_value_assert),
149        unit_test(decrement_value_fail),
150    };
151    return run_tests(tests, "assert_module");
152}
153```
154
155## Assert Macros
156
157Cmockery provides an assortment of assert macros that tests applications should use use in preference to the C standard library's assert macro. On an assertion failure a Cmockery assert macro will write the failure to the standard error stream and signal a test failure. Due to limitations of the C language the general C standard library `assert()` and Cmockery's `assert_true()` and `assert_false()` macros can only display the expression that caused the assert failure. Cmockery's type specific assert macros, `assert_{type}_equal()` and `assert_{type}_not_equal()`, display the data that caused the assertion failure which increases data visibility aiding debugging of failing test cases.
158
159### Using assert\_{type}\_equal() macros
160
161#### assert\_macro.c
162
163```c
164#include <string.h>
165
166static const char* status_code_strings[] = {
167    "Address not found",
168    "Connection dropped",
169    "Connection timed out",
170};
171
172const char* get_status_code_string(const unsigned int status_code) {
173    return status_code_strings[status_code];
174};
175
176unsigned int string_to_status_code(const char* const status_code_string) {
177    unsigned int i;
178    for (i = 0; i < sizeof(status_code_strings) /
179                    sizeof(status_code_strings[0]); i++) {
180        if (strcmp(status_code_strings[i], status_code_string) == 0) {
181            return i;
182        }
183    }
184    return ~0U;
185}
186```
187
188#### assert\_macro\_test.c
189
190```c
191#include <stdarg.h>
192#include <stddef.h>
193#include <setjmp.h>
194#include <cmockery.h>
195
196extern const char* get_status_code_string(const unsigned int status_code);
197extern unsigned int string_to_status_code(
198    const char* const status_code_string);
199
200/* This test will fail since the string returned by get_status_code_string(0)
201 * doesn't match "Connection timed out". */
202void get_status_code_string_test(void **state) {
203    assert_string_equal(get_status_code_string(0), "Address not found");
204    assert_string_equal(get_status_code_string(1), "Connection timed out");
205}
206
207// This test will fail since the status code of "Connection timed out" isn't 1
208void string_to_status_code_test(void **state) {
209    assert_int_equal(string_to_status_code("Address not found"), 0);
210    assert_int_equal(string_to_status_code("Connection timed out"), 1);
211}
212
213int main(int argc, char *argv[]) {
214    const UnitTest tests[] = {
215        unit_test(get_status_code_string_test),
216        unit_test(string_to_status_code_test),
217    };
218    return run_tests(tests, "assert_macro");
219}
220```
221
222## Dynamic Memory Allocation
223
224To test for memory leaks, buffer overflows and underflows a module being tested by Cmockery should replace calls to `malloc()`, `calloc()` and `free()` to `test_malloc()`, `test_calloc()` and `test_free()` respectively. This can be handled by including `cmockery_override.h`. Each time a block is deallocated using `test_free()` it is checked for corruption, if a corrupt block is found a test failure is signalled. All blocks allocated using the `test_*()` allocation functions are tracked by the Cmockery library. When a test completes if any allocated blocks (memory leaks) remain they are reported and a test failure is signalled.
225
226For simplicity Cmockery currently executes all tests in one process. Therefore all test cases in a test application share a single address space which means memory corruption from a single test case could potentially cause the test application to exit prematurely.
227
228### Using Cmockery's Allocators
229
230#### allocate\_module.c
231
232```c
233#include <malloc.h>
234#ifdef UNIT_TESTING
235/* Must be last include */
236#include <cmockery/cmockery_override.h>
237#endif
238
239void leak_memory(void);
240void buffer_overflow(void);
241void buffer_underflow(void);
242
243void leak_memory(void) {
244    int * const temporary = (int*)malloc(sizeof(int));
245    *temporary = 0;
246}
247
248void buffer_overflow(void) {
249    char * const memory = (char*)malloc(sizeof(int));
250    memory[sizeof(int)] = '!';
251    free(memory);
252}
253
254void buffer_underflow(void) {
255    char * const memory = (char*)malloc(sizeof(int));
256    memory[-1] = '!';
257    free(memory);
258}
259```
260
261#### allocate\_module\_test.c
262
263```c
264#include <stdarg.h>
265#include <stddef.h>
266#include <setjmp.h>
267#include <inttypes.h>
268#include <cmockery.h>
269
270extern void leak_memory();
271extern void buffer_overflow();
272extern void buffer_underflow();
273
274// Test case that fails as leak_memory() leaks a dynamically allocated block.
275static void leak_memory_test(void **state) {
276    leak_memory();
277}
278
279// Test case that fails as buffer_overflow() corrupts an allocated block.
280static void buffer_overflow_test(void **state) {
281    buffer_overflow();
282}
283
284// Test case that fails as buffer_underflow() corrupts an allocated block.
285static void buffer_underflow_test(void **state) {
286    buffer_underflow();
287}
288
289int main(int argc, char* argv[]) {
290    const UnitTest tests[] = {
291        unit_test(leak_memory_test),
292        unit_test(buffer_overflow_test),
293        unit_test(buffer_underflow_test),
294    };
295    return run_tests(tests, "allocate_module_test");
296}
297```
298
299## Mock Functions
300
301A unit test should ideally isolate the function or module being tested from any external dependencies. This can be performed using mock functions that are either statically or dynamically linked with the module being tested. Mock functions must be statically linked when the code being tested directly references external functions. Dynamic linking is simply the process of setting a function pointer in a table used by the tested module to reference a mock function defined in the unit test.
302
303### Return Values
304
305In order to simplify the implementation of mock functions Cmockery provides functionality which stores return values for mock functions in each test case using will_`return()`. These values are then returned by each mock function using calls to `mock()`. Values passed to `will_return()` are added to a queue for each function specified. Each successive call to `mock()` from a function removes a return value from the queue. This makes it possible for a mock function to use multiple calls to `mock()` to return output parameters in addition to a return value. In addition this allows the specification of return values for multiple calls to a mock function.
306
307#### Using will\_return()
308
309##### database.h
310
311```c
312typedef struct DatabaseConnection DatabaseConnection;
313
314/* Function that takes an SQL query string and sets results to an array of
315 * pointers with the result of the query.  The value returned specifies the
316 * number of items in the returned array of results.  The returned array of
317 * results are statically allocated and should not be deallocated using free()
318 */
319typedef unsigned int (*QueryDatabase)(
320    DatabaseConnection* const connection, const char * const query_string,
321    void *** const results);
322
323// Connection to a database.
324struct DatabaseConnection {
325    const char *url;
326    unsigned int port;
327    QueryDatabase query_database;
328};
329```
330
331##### customer\_database.c
332
333```c
334#include <stdint.h>
335#include <stddef.h>
336#include <stdio.h>
337#include <database.h>
338#ifdef _WIN32
339#define snprintf _snprintf
340#endif // _WIN32
341
342DatabaseConnection* connect_to_customer_database(void);
343unsigned int get_customer_id_by_name(
344        DatabaseConnection * const connection,
345        const char * const customer_name);
346
347// Connect to the database containing customer information.
348DatabaseConnection* connect_to_customer_database(void) {
349    return connect_to_database("customers.abcd.org", 321);
350}
351
352/* Find the ID of a customer by his/her name returning a value > 0 if
353 * successful, 0 otherwise. */
354unsigned int get_customer_id_by_name(
355        DatabaseConnection * const connection,
356        const char * const customer_name) {
357    char query_string[256];
358    int number_of_results;
359    void **results;
360    snprintf(query_string, sizeof(query_string),
361             "SELECT ID FROM CUSTOMERS WHERE NAME = %s", customer_name);
362    number_of_results = connection->query_database(connection, query_string,
363                                                   &results);
364    if (number_of_results != 1) {
365        return -1;
366    }
367    return (uintptr_t)results[0];
368}
369```
370
371##### customer\_database\_test.c
372
373```c
374#include <stdarg.h>
375#include <stddef.h>
376#include <setjmp.h>
377#include <inttypes.h>
378#include <cmockery.h>
379#include <database.h>
380
381extern DatabaseConnection* connect_to_customer_database();
382extern unsigned int get_customer_id_by_name(
383    DatabaseConnection * const connection, const char * const customer_name);
384
385// Mock query database function.
386static unsigned int mock_query_database(
387        DatabaseConnection* const connection, const char * const query_string,
388        void *** const results) {
389    *results = (void**)((uintptr_t)mock());
390    return (unsigned int)mock();
391}
392
393// Mock of the connect to database function.
394DatabaseConnection* connect_to_database(const char * const database_url,
395                                        const unsigned int port) {
396    return (DatabaseConnection*)((uintptr_t)mock());
397}
398
399static void test_connect_to_customer_database(void **state) {
400    will_return(connect_to_database, 0x0DA7ABA53);
401    assert_int_equal((uintptr_t)connect_to_customer_database(), 0x0DA7ABA53);
402}
403
404/* This test fails as the mock function connect_to_database() will have no
405 * value to return. */
406static void test_fail_connect_to_customer_database(void **state) {
407    assert_true(connect_to_customer_database() ==
408                (DatabaseConnection*)0x0DA7ABA53);
409}
410
411static void test_get_customer_id_by_name(void **state) {
412    DatabaseConnection connection = {
413        "somedatabase.somewhere.com", 12345678, mock_query_database
414    };
415    // Return a single customer ID when mock_query_database() is called.
416    int customer_ids = 543;
417    will_return(mock_query_database, &customer_ids);
418    will_return(mock_query_database, 1);
419    assert_int_equal(get_customer_id_by_name(&connection, "john doe"), 543);
420}
421
422int main(void) {
423    const UnitTest tests[] = {
424        unit_test(test_connect_to_customer_database),
425        unit_test(test_get_customer_id_by_name),
426	unit_test(test_fail_connect_to_customer_database),
427    };
428    return run_tests(tests, "customer_database");
429}
430```
431
432
433
434## Checking Parameters
435
436In addition to storing the return values of mock functions, Cmockery provides functionality to store expected values for mock function parameters using the `expect_*()` functions provided. A mock function parameter can then be validated using the `check_expected()` macro.
437
438Successive calls to `expect_*()` macros for a parameter queues values to check the specified parameter. `check_expected()` checks a function parameter against the next value queued using `expect_*()`, if the parameter check fails a test failure is signalled. In addition if `check_expected()` is called and no more parameter values are queued a test failure occurs.
439
440### Using expect\_\*()
441
442#### product\_database.c
443
444```c
445#include <database.h>
446
447DatabaseConnection* connect_to_product_database(void);
448
449// Connect to the database containing customer information.
450DatabaseConnection* connect_to_product_database(void) {
451    return connect_to_database("products.abcd.org", 322);
452}
453```
454
455#### product\_database\_test.c
456
457```c
458#include <stdarg.h>
459#include <stddef.h>
460#include <setjmp.h>
461#include <inttypes.h>
462#include <cmockery.h>
463#include <database.h>
464
465extern DatabaseConnection* connect_to_product_database(void);
466
467/* Mock connect to database function.
468 * NOTE: This mock function is very general could be shared between tests
469 * that use the imaginary database.h module. */
470DatabaseConnection* connect_to_database(const char * const url,
471                                        const unsigned int port) {
472    check_expected(url);
473    check_expected(port);
474    return (DatabaseConnection*)((uintptr_t)mock());
475}
476
477static void test_connect_to_product_database(void **state) {
478    expect_string(connect_to_database, url, "products.abcd.org");
479    expect_value(connect_to_database, port, 322);
480    will_return(connect_to_database, 0xDA7ABA53);
481    assert_int_equal((uintptr_t)connect_to_product_database(), 0xDA7ABA53);
482}
483
484/* This test will fail since the expected URL is different to the URL that is
485 * passed to connect_to_database() by connect_to_product_database(). */
486static void test_connect_to_product_database_bad_url(void **state) {
487    expect_string(connect_to_database, url, "products.abcd.com");
488    expect_value(connect_to_database, port, 322);
489    will_return(connect_to_database, 0xDA7ABA53);
490    assert_int_equal((uintptr_t)connect_to_product_database(), 0xDA7ABA53);
491}
492
493/* This test will fail since the mock connect_to_database() will attempt to
494 * retrieve a value for the parameter port which isn't specified by this
495 * test function. */
496static void test_connect_to_product_database_missing_parameter(void **state) {
497    expect_string(connect_to_database, url, "products.abcd.org");
498    will_return(connect_to_database, 0xDA7ABA53);
499    assert_int_equal((uintptr_t)connect_to_product_database(), 0xDA7ABA53);
500}
501
502int main(void) {
503    const UnitTest tests[] = {
504        unit_test(test_connect_to_product_database),
505        unit_test(test_connect_to_product_database_bad_url),
506        unit_test(test_connect_to_product_database_missing_parameter),
507    };
508    return run_tests(tests, "product_database");
509}
510```
511
512## Test State
513
514Cmockery allows the specification of multiple setup and tear down functions for each test case. Setup functions, specified by the unit_test_setup() or unit_test_setup_teardown() macros allow common initialization to be shared between multiple test cases. In addition, tear down functions, specified by the unit_test_teardown() or unit_test_setup_teardown() macros provide a code path that is always executed for a test case even when it fails.
515
516### Using unit\_test\_setup\_teardown()
517
518#### key\_value.c
519
520```c
521#include <stddef.h>
522#include <stdlib.h>
523#include <string.h>
524
525#include "key_value.h"
526
527static KeyValue *key_values = NULL;
528static unsigned int number_of_key_values = 0;
529
530void set_key_values(KeyValue * const new_key_values,
531                    const unsigned int new_number_of_key_values) {
532    key_values = new_key_values;
533    number_of_key_values = new_number_of_key_values;
534}
535
536// Compare two key members of KeyValue structures.
537static int key_value_compare_keys(const void *a, const void *b) {
538    return (int)((KeyValue*)a)->key - (int)((KeyValue*)b)->key;
539}
540
541// Search an array of key value pairs for the item with the specified value.
542KeyValue* find_item_by_value(const char * const value) {
543  unsigned int i;
544    for (i = 0; i < number_of_key_values; i++) {
545        if (strcmp(key_values[i].value, value) == 0) {
546            return &key_values[i];
547        }
548    }
549    return NULL;
550}
551
552// Sort an array of key value pairs by key.
553void sort_items_by_key(void) {
554    qsort(key_values, number_of_key_values, sizeof(*key_values),
555          key_value_compare_keys);
556}
557```
558
559#### key\_value\_test.c
560
561```c
562#include <stdarg.h>
563#include <stddef.h>
564#include <setjmp.h>
565#include <string.h>
566#include <inttypes.h>
567#include <cmockery.h>
568
569#include "key_value.h"
570
571static KeyValue key_values[] = {
572    { 10, "this" },
573    { 52, "test" },
574    { 20, "a" },
575    { 13, "is" },
576};
577
578static void create_key_values(void **state) {
579    KeyValue * const items = (KeyValue*)test_malloc(sizeof(key_values));
580    memcpy(items, key_values, sizeof(key_values));
581    *state = (void*)items;
582    set_key_values(items, sizeof(key_values) / sizeof(key_values[0]));
583}
584
585static void destroy_key_values(void **state) {
586    test_free(*state);
587    set_key_values(NULL, 0);
588}
589
590static void test_find_item_by_value(void **state) {
591    unsigned int i;
592    for (i = 0; i < sizeof(key_values) / sizeof(key_values[0]); i++) {
593        KeyValue * const found  = find_item_by_value(key_values[i].value);
594        assert_true(found != NULL);
595        assert_int_equal(found->key, key_values[i].key);
596        assert_string_equal(found->value, key_values[i].value);
597    }
598}
599
600static void test_sort_items_by_key(void **state) {
601    unsigned int i;
602    KeyValue * const kv = *state;
603    sort_items_by_key();
604    for (i = 1; i < sizeof(key_values) / sizeof(key_values[0]); i++) {
605        assert_true(kv[i - 1].key < kv[i].key);
606    }
607}
608
609int main(void) {
610    const UnitTest tests[] = {
611        unit_test_setup_teardown(test_find_item_by_value, create_key_values,
612                                 destroy_key_values),
613        unit_test_setup_teardown(test_sort_items_by_key, create_key_values,
614                                 destroy_key_values),
615    };
616    return run_tests(tests, "key_value");
617}
618```
619
620## Example
621
622A small command line calculator `calculator.c` application and test application that full exercises the calculator application `calculator_test.c` are provided as an example of Cmockery's features discussed in this document.
623