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