Imre Kis | a21712e | 2019-10-08 12:56:59 +0200 | [diff] [blame] | 1 | CppUTest |
| 2 | ======== |
| 3 | |
| 4 | This document is based on CppUTest v3.8. CppUtest is a unit testing framework for testing C and C++ code. This document |
| 5 | introduces the basic features of the framework. For further information check the `official manual of CppUTest`_. |
| 6 | |
| 7 | |
| 8 | Why CppUTest? |
| 9 | ------------- |
| 10 | |
| 11 | First of all it was not our goal to develop a new unit testing framework while plenty of open source solutions are already |
| 12 | available. There were no special requirements agains the unit testing framework that would rule out all existing frameworks so |
| 13 | we only had to choose a suitable one for our current and possible future needs. |
| 14 | |
| 15 | We ended up selecting CppUTest because of its small footprint and easy portability. It also goes along with the standard xUnit |
| 16 | frameworks' principles and provides a standard interface to the outside world. Some details are listed below. |
| 17 | |
| 18 | - C/C++ support |
| 19 | - Small footprint (compared to Google Test) |
| 20 | - Easy to use on embedded systems |
| 21 | - Built-in mocking system (CppUMock) |
| 22 | - Implements four-phase testing pattern (setup, exercise, verify, teardown) |
| 23 | - Selective run of test cases |
| 24 | - Standard output format (JUnit, TeamCity) |
| 25 | |
| 26 | |
| 27 | Assertions |
| 28 | ---------- |
| 29 | |
| 30 | Generally is a good practice to use more specific assertions because it can output more informative error messages in case of a |
| 31 | failure. The generic form or assertions is ``assert(expected, actual)``. Each assert type has a _TEXT variant for user defined |
| 32 | error messages as last parameter. |
| 33 | |
| 34 | - Boolean |
| 35 | |
| 36 | - ``CHECK(condition)`` - Same as ``CHECK_TRUE`` |
| 37 | - ``CHECK_TRUE(condition)`` |
| 38 | - ``CHECK_FALSE(condition)`` |
| 39 | |
| 40 | - C string |
| 41 | |
| 42 | - ``STRCMP_EQUAL(expected, actual)`` |
| 43 | - ``STRNCMP_EQUAL(expected, actual, length)`` |
| 44 | - ``STRCMP_NOCASE_EQUAL(expected, actual)`` |
| 45 | - ``STRCMP_CONTAINS(expected, actual)`` |
| 46 | - ``STRCMP_NOCASE_CONTAINS(expected, actual)`` |
| 47 | |
| 48 | - Integer |
| 49 | |
| 50 | - ``LONGS_EQUAL(expected, actual)`` |
| 51 | - ``UNSIGNED_LONGS_EQUAL(expected, actual)`` |
| 52 | - ``LONGLONGS_EQUAL(expected, actual)`` |
| 53 | - ``UNSIGNED_LONGLONGS_EQUAL(expected, actual)`` |
| 54 | - ``BYTES_EQUAL(expected, actual)`` |
| 55 | - ``SIGNED_BYTES_EQUAL(expected, actual)`` |
| 56 | - ``POINTERS_EQUAL(expected, actual)`` |
| 57 | - ``FUNCTIONPOINTERS_EQUAL(expected, actual)`` |
| 58 | |
| 59 | - Enums |
| 60 | |
| 61 | - ``ENUMS_EQUAL_INT(expected, actual)`` |
| 62 | - ``ENUMS_EQUAL_TYPE(underlying_type, expected, actual)`` |
| 63 | |
| 64 | - Other assertions |
| 65 | |
| 66 | - ``CHECK_EQUAL(expected, actual)`` - Requires ``operator=`` and ``StringFrom(type)`` to be implemented |
| 67 | - ``CHECK_COMPARE(first, relop, second)`` - Same as ``CHECK_EQUAL`` but with any type of compare |
| 68 | - ``DOUBLES_EQUAL(expected, actual, threshold)`` |
| 69 | - ``MEMCMP_EQUAL(expected, actual, size)`` |
| 70 | - ``BITS_EQUAL(expected, actual, mask)`` |
| 71 | - ``FAIL()`` or ``FAIL_TEST()`` - Test case fails if called |
| 72 | - ``CHECK_THROWS(expected, expression)`` - Catching C++ exceptions |
| 73 | |
| 74 | - Miscellaneous macros |
| 75 | |
| 76 | - ``IGNORE_TEST`` - Same as ``TEST`` but it’s not called |
| 77 | - ``TEST_EXIT`` - Exists test |
| 78 | - ``UT_CRASH()`` - Crashes the test which is easy to catch with debugger |
| 79 | - ``UT_PRINT(text)`` - Generic print function |
| 80 | |
| 81 | |
| 82 | Test runner |
| 83 | ----------- |
| 84 | |
| 85 | Test cases are collected automatically. Under the hood the ``TEST`` macros are creating global instances of classes and their |
| 86 | constructor registers the test cases into the test registry. This happens before entering the ``main`` function. In the ``main`` |
| 87 | function only the ``RUN_ALL_TESTS`` macro needs to be placed with the command line arguments passed to it. On executing the |
| 88 | binary the command line arguments will control the behaviour of the test process. |
| 89 | |
| 90 | .. code-block:: C++ |
| 91 | |
| 92 | #include "CppUTest/CommandLineTestRunner.h" |
| 93 | |
| 94 | int main(int argc, char* argv[]) { |
| 95 | return RUN_ALL_TESTS(argc, argv); |
| 96 | } |
| 97 | |
| 98 | The default ``main`` implementation is added to all unit test suites by the |
| 99 | build system. |
| 100 | |
| 101 | |
| 102 | Command line options |
| 103 | -------------------- |
| 104 | |
| 105 | Command line options are available mainly for configuring the output format of |
| 106 | the test binary and for filtering test groups or cases. |
| 107 | |
| 108 | - Output |
| 109 | |
| 110 | - ``-v`` - Prints each test name before running them |
| 111 | - ``-c`` - Colorized output |
| 112 | - ``-o{normal, junit, teamcity}`` - Output format, junit can be processed by |
| 113 | most CIs |
| 114 | - ``-k packageName`` - Package name for junit output |
| 115 | - ``-lg`` - List test groups |
| 116 | - ``-ln`` - List test cases |
| 117 | |
| 118 | - Other |
| 119 | |
| 120 | - ``-p`` - Runs each test case in separate processes |
| 121 | - ``-ri`` - Runs ignored test cases |
| 122 | - ``-r#`` - Repeats testing ``#`` times |
| 123 | - ``-s seed`` - Shuffles tests |
| 124 | |
| 125 | - Filtering test cases |
| 126 | |
| 127 | - ``"TEST(groupName, testName)"`` - Running single test |
| 128 | - ``"IGNORE_TEST(groupName, testName)"`` -- Running single ignored test |
| 129 | - ``-g text`` - Runing groups containing text |
| 130 | - ``-n text`` - Runing tests containing text |
| 131 | - ``-sg text`` - Runing groups matching text |
| 132 | - ``-sn text`` - Runing tests matching text |
| 133 | - ``-xg text`` - Excluding groups containing text |
| 134 | - ``-xn text`` - Excluding tests containing text |
| 135 | - ``-xsg text`` - Excluding groups matching text |
| 136 | - ``-xsn text`` - Excluding tests matching text |
| 137 | |
| 138 | |
| 139 | Troubleshooting |
| 140 | --------------- |
| 141 | |
| 142 | Output messages |
| 143 | ^^^^^^^^^^^^^^^ |
| 144 | |
| 145 | When one of tests fails the first step is to run it separately and check its |
| 146 | output message. Usually it shows the exact line of the file where the error |
| 147 | happened. |
| 148 | |
| 149 | :: |
| 150 | |
| 151 | test_memcmp.cpp:17: error: Failure in TEST(memcmp, empty) |
| 152 | expected <1 0x1> |
| 153 | but was <0 0x0> |
| 154 | |
| 155 | The executed tests can be followed by adding ``-v`` command line option. |
| 156 | |
| 157 | :: |
| 158 | |
| 159 | ./memcmp -v |
| 160 | TEST(memcmp, different) - 0 ms |
| 161 | TEST(memcmp, same) - 0 ms |
| 162 | TEST(memcmp, empty) - 0 ms |
| 163 | |
| 164 | OK (3 tests, 3 ran, 1 checks, 0 ignored, 0 filtered out, 0 ms) |
| 165 | |
| 166 | |
| 167 | Catching failure with debugger |
| 168 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 169 | |
| 170 | If a failure happens in a helper function or in a loop where the assertion |
| 171 | is called multiple times it is harder to get the exact environment of a failure. |
| 172 | In this case it's a good practice to put a ``UT_CRASH()`` call into a |
| 173 | conditional block which hits when the failure happens. This way the debugger can |
| 174 | stop on failure because the code emits a signal. |
| 175 | |
| 176 | .. code-block:: C++ |
| 177 | |
| 178 | TEST(magic, iterate) { |
| 179 | int result; |
| 180 | |
| 181 | for(int i = 0; i < 1000; i++) { |
| 182 | result = magic_function(i); |
| 183 | |
| 184 | // Debug code |
| 185 | if (result) { |
| 186 | UT_CRASH(); |
| 187 | } |
| 188 | |
| 189 | LONGS_EQUAL(0, result); |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | |
| 194 | Using ``FAIL`` macro |
| 195 | ^^^^^^^^^^^^^^^^^^^^ |
| 196 | |
| 197 | It's recommended to use ``FAIL`` macro in conditions that should never occur in |
| 198 | tests. For example if a test case loads test data from an external file but the |
| 199 | file could not be opened the ``FAIL`` macro should be used with an informative |
| 200 | message. |
| 201 | |
| 202 | .. code-block:: C++ |
| 203 | |
| 204 | fd = open("test.bin", O_RDONLY); |
| 205 | if (fd < 0) { |
| 206 | FAIL("test.bin open failed"); |
| 207 | } |
| 208 | |
| 209 | |
| 210 | Interference between test cases |
| 211 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 212 | |
| 213 | Test cases can interfere if there's a global resource which was not restored to |
| 214 | its original state after leaving a test case. This can be hard to find but at |
| 215 | least the it's easy to make sure that this is root case of an error. Let's |
| 216 | assume there's a global variable which is set during the test case but it |
| 217 | original value is not restore at the end. CppUTest has an command line option |
| 218 | for running each test case in separate process. This makes the global variable |
| 219 | to have its original value at the beginning of the test cases. Basically if the |
| 220 | test works by passing argument ``-p`` when running but fails without it, there's |
| 221 | a good chance for having an interference between test cases. |
| 222 | |
| 223 | .. code-block:: C++ |
| 224 | |
| 225 | int x = 0; |
| 226 | |
| 227 | TEST_GROUP(crosstalk) { |
| 228 | }; |
| 229 | |
| 230 | TEST(crosstalk, a) { |
| 231 | LONGS_EQUAL(0, x); |
| 232 | x = 1; |
| 233 | } |
| 234 | |
| 235 | TEST(crosstalk, b) { |
| 236 | LONGS_EQUAL(0, x); |
| 237 | x = 1; |
| 238 | } |
| 239 | |
| 240 | TEST(crosstalk, c) { |
| 241 | LONGS_EQUAL(0, x); |
| 242 | x = 1; |
| 243 | } |
| 244 | |
| 245 | By running the test executable with different command line arguments it produces |
| 246 | a different result. |
| 247 | |
| 248 | .. code-block:: |
| 249 | |
| 250 | ./crosstalk -v |
| 251 | |
| 252 | TEST(crosstalk, c) - 0 ms |
| 253 | TEST(crosstalk, b) |
| 254 | test_crosstalk.cpp:37: error: |
| 255 | Failure in TEST(crosstalk, b) |
| 256 | expected <0 0x0> |
| 257 | but was <1 0x1> |
| 258 | |
| 259 | - 0 ms |
| 260 | TEST(crosstalk, a) |
| 261 | test_crosstalk.cpp:32: error: Failure in TEST(crosstalk, a) |
| 262 | expected <0 0x0> |
| 263 | but was <1 0x1> |
| 264 | |
| 265 | - 0 ms |
| 266 | |
| 267 | Errors (2 failures, 3 tests, 3 ran, 3 checks, 0 ignored, 0 filtered out, 0 ms) |
| 268 | |
| 269 | ./crosstalk -v -p |
| 270 | TEST(crosstalk, c) - 1 ms |
| 271 | TEST(crosstalk, b) - 0 ms |
| 272 | TEST(crosstalk, a) - 0 ms |
| 273 | |
| 274 | OK (3 tests, 0 ran, 0 checks, 0 ignored, 0 filtered out, 2 ms) |
| 275 | |
| 276 | |
| 277 | -------------- |
| 278 | |
| 279 | *Copyright (c) 2019-2021, Arm Limited. All rights reserved.* |
| 280 | |
| 281 | .. _`official manual of CppUTest`: https://cpputest.github.io/ |