├── .gitignore ├── docs ├── unit_test.png └── unit_test.uxf ├── tests ├── cpputest_common.cmake ├── CMakeLists.txt ├── pythagorean_ut │ ├── main.cpp │ ├── square_root_mock.cpp │ ├── CMakeLists.txt │ └── pythagorean_ut.cpp └── cpputest_external_proj.cmake ├── src ├── square_root.h ├── pythagorean.h ├── square_root.c ├── pythagorean.c ├── main.c └── CMakeLists.txt ├── CMakeLists.txt ├── .travis.yml ├── LICENSE ├── .github └── workflows │ └── cmake.yml └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | cmake-build-*/ -------------------------------------------------------------------------------- /docs/unit_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxpeng/cpputest_example/HEAD/docs/unit_test.png -------------------------------------------------------------------------------- /tests/cpputest_common.cmake: -------------------------------------------------------------------------------- 1 | # define the location to install the external project(s). 2 | set(EXTERNAL_INSTALL_LOCATION ${CMAKE_BINARY_DIR}/external) -------------------------------------------------------------------------------- /src/square_root.h: -------------------------------------------------------------------------------- 1 | #ifndef SQUARE_ROOT_H 2 | #define SQUARE_ROOT_H 3 | 4 | 5 | float SquareRoot_sqrt(float number); 6 | 7 | 8 | #endif //SQUARE_ROOT_H 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(cpputest_example) 4 | 5 | include(CTest) 6 | 7 | add_subdirectory(src) 8 | add_subdirectory(tests) 9 | -------------------------------------------------------------------------------- /src/pythagorean.h: -------------------------------------------------------------------------------- 1 | #ifndef PYTHAGOREAN_H 2 | #define PYTHAGOREAN_H 3 | 4 | 5 | float Pythagorean_hypotenuse(float x, float y); 6 | 7 | 8 | #endif //PYTHAGOREAN_H 9 | -------------------------------------------------------------------------------- /src/square_root.c: -------------------------------------------------------------------------------- 1 | #include "square_root.h" 2 | #include 3 | 4 | 5 | float SquareRoot_sqrt(float number) 6 | { 7 | return (float)sqrt(number); 8 | } 9 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # load and build cpputest/cppumock library. 2 | include(cpputest_external_proj.cmake) 3 | 4 | # build the unit test(s). 5 | add_subdirectory(pythagorean_ut) -------------------------------------------------------------------------------- /tests/pythagorean_ut/main.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/CommandLineTestRunner.h" 2 | 3 | int main(int ac, char** av) 4 | { 5 | return CommandLineTestRunner::RunAllTests(ac, av); 6 | } 7 | -------------------------------------------------------------------------------- /src/pythagorean.c: -------------------------------------------------------------------------------- 1 | #include "pythagorean.h" 2 | #include "square_root.h" 3 | 4 | 5 | float Pythagorean_hypotenuse(float x, float y) 6 | { 7 | float hypotenuse = SquareRoot_sqrt(x*x + y*y); 8 | return hypotenuse; 9 | } 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | compiler: 4 | - clang 5 | - gcc 6 | 7 | script: 8 | - mkdir build 9 | - cd build 10 | - cmake .. 11 | - make 12 | - make test 13 | 14 | before_install: 15 | # Print used compiler versions. 16 | - gcc --version 17 | - clang --version 18 | 19 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pythagorean.h" 3 | 4 | int main(void) 5 | { 6 | int x = 30; 7 | int y = 40; 8 | float hypotenuse = Pythagorean_hypotenuse(x, y); 9 | 10 | printf("Hypotenuse of a right triangle with 2 sides as (%d, %d) is %0.1f.\n", x, y, hypotenuse); 11 | 12 | return 0; 13 | } -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_C_STANDARD 99) 2 | 3 | set(CURRENT_EXE_NAME cpputest_exasple_pythagorean) 4 | set(SOURCE_FILES main.c pythagorean.c square_root.c) 5 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 6 | add_executable(${CURRENT_EXE_NAME} ${SOURCE_FILES}) 7 | 8 | # link against math library. 9 | target_link_libraries(${CURRENT_EXE_NAME} m) 10 | install(TARGETS ${CURRENT_EXE_NAME} DESTINATION bin) 11 | -------------------------------------------------------------------------------- /tests/pythagorean_ut/square_root_mock.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTestExt/MockSupport.h" 2 | 3 | extern "C" 4 | { 5 | #include 6 | #include "square_root.h" 7 | } 8 | 9 | 10 | float SquareRoot_sqrt(float number) 11 | { 12 | printf("\nSquareRoot_sqrt gets called with parameter: %f.\n", number); 13 | 14 | return float(mock().actualCall(__func__) 15 | .withParameter("number", number) 16 | .returnDoubleValue()); 17 | } 18 | -------------------------------------------------------------------------------- /tests/pythagorean_ut/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # build the unit test executable. 2 | set(CURRENT_EXE_NAME pythagorean_ut) 3 | 4 | set(SOURCE_FILES 5 | main.cpp 6 | pythagorean_ut.cpp 7 | ${PROJECT_SOURCE_DIR}/src/pythagorean.c 8 | square_root_mock.cpp) 9 | add_executable(${CURRENT_EXE_NAME} ${SOURCE_FILES}) 10 | add_dependencies(${CURRENT_EXE_NAME} cpputest) 11 | target_include_directories(${CURRENT_EXE_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src) 12 | target_link_libraries(${CURRENT_EXE_NAME} CppUTest CppUTestExt) 13 | 14 | # add the test to the project to be run by ctest. 15 | add_test(pythagorean_test ${CURRENT_EXE_NAME}) -------------------------------------------------------------------------------- /tests/cpputest_external_proj.cmake: -------------------------------------------------------------------------------- 1 | # include cpputest common settings. 2 | include(cpputest_common.cmake) 3 | 4 | # build CppUTest and CppUMock libraries. 5 | include(ExternalProject) 6 | ExternalProject_Add(cpputest 7 | GIT_REPOSITORY https://github.com/cpputest/cpputest.git 8 | GIT_TAG v4.0 9 | INSTALL_DIR ${EXTERNAL_INSTALL_LOCATION} 10 | CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= 11 | ) 12 | 13 | # add the installed cpputest and cppumock include directories to those the 14 | # compiler uses to search for include files. 15 | include_directories(${EXTERNAL_INSTALL_LOCATION}/include) 16 | 17 | # adds the paths of installed cpputest and cppumock libraries in which the linker 18 | # should search for libraries. 19 | link_directories(${EXTERNAL_INSTALL_LOCATION}/lib) 20 | 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Max Peng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/pythagorean_ut/pythagorean_ut.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "CppUTestExt/MockSupport.h" 3 | 4 | extern "C" 5 | { 6 | #include "pythagorean.h" 7 | } 8 | 9 | 10 | TEST_GROUP(Pythagorean) 11 | { 12 | void setup() { 13 | } 14 | 15 | 16 | void teardown() { 17 | mock().clear(); 18 | } 19 | 20 | }; 21 | 22 | 23 | TEST(Pythagorean, simpleTest) 24 | { 25 | // arrange 26 | mock().expectOneCall("SquareRoot_sqrt") 27 | .withParameter("number", 25.0) 28 | .andReturnValue(5.0f); 29 | 30 | // act 31 | float hypotenuse = Pythagorean_hypotenuse(3, 4); 32 | 33 | // assert 34 | mock().checkExpectations(); 35 | CHECK_EQUAL(5.0f, hypotenuse); 36 | } 37 | 38 | 39 | TEST(Pythagorean, BlackBoxTest) 40 | { 41 | // arrange 42 | mock().expectOneCall("SquareRoot_sqrt") 43 | .withParameter("number", 100.0) 44 | .andReturnValue(100.0f); // intentionally set the result as 100.0 instead of 10.0. 45 | 46 | // act 47 | float hypotenuse = Pythagorean_hypotenuse(6, 8); 48 | 49 | // assert 50 | mock().checkExpectations(); 51 | CHECK_EQUAL(100.0f, hypotenuse); 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - name: Configure CMake 24 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 25 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 26 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE 27 | 28 | - name: Build 29 | # Build your program with the given configuration 30 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --target all -v 31 | 32 | - name: Test 33 | working-directory: ${{github.workspace}}/build 34 | # Execute tests defined by the CMake configuration. 35 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 36 | run: ctest -C ${{env.BUILD_TYPE}} -V 37 | 38 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # CppUTest Example 2 | 3 | This is an example about using [CppUTest](http://cpputest.github.io/) 4 | to mock C files for unit test. 5 | 6 | 7 | ## Prerequisites 8 | 9 | 1. [CMake](https://cmake.org/) 10 | 2. GNU C/C++ compiler 11 | 12 | ## Directory Structure 13 | 14 | ``` 15 | cpputest_example 16 | ├── CMakeLists.txt --> main cmake configuration file 17 | ├── LICENSE 18 | ├── readme.md 19 | ├── src --> pythagorean example 20 | │   ├── CMakeLists.txt 21 | │   ├── main.c 22 | │   ├── pythagorean.c --> unit under test 23 | │   ├── pythagorean.h 24 | │   ├── square_root.c --> dependency of pythagorean.c 25 | │   └── square_root.h 26 | └── tests --> unit test against pythagorean.c 27 |    ├── CMakeLists.txt 28 |    ├── cpputest_common.cmake 29 |    ├── cpputest_external_proj.cmake --> cmake configuration file to build CppUTest 30 | │ and CppUMock as an external project. 31 |    └── pythagorean_ut --> unit test using CppUTest and CppUMock 32 |    ├── CMakeLists.txt 33 |    ├── main.cpp 34 |    ├── pythagorean_ut.cpp 35 |    └── square_root_mock.cpp 36 | ``` 37 | 38 | ## Unit Test 39 | 40 | The `src` folder contains the source code of an example called `cpputest_example_pythagorean` after it is built by CMake. 41 | 42 | * `pythagorean.c` implements a function called `Pythagorean_hypotenuse(x, y)`, which calculates the `hypotenuse` of a right-angled triangle with `x` and `y` as the two sides of right angle. 43 | * `square_root.c` provides a function called `SquareRoot_sqrt(number)`, which returns the square root of the parameter `number`. 44 | * The implementation of `Pythagorean_hypotenuse(x, y)` calls `SquareRoot_sqrt(number)`. That is, `Pythagorean_hypotenuse(x, y)` depends on `SquareRoot_sqrt(number)`. 45 | 46 | We want to test `Pythagorean_hypotenuse(x, y)` function provided by `pythagorean.c`. From the unit test point of view, `Pythagorean_hypotenuse(x, y)` is the `System Under Test (SUT)`, `SquareRoot_sqrt(number)` is its dependency. 47 | 48 | ![Unit Test](docs/unit_test.png) 49 | 50 | ### Test 1 - Simple Test 51 | 52 | ```c 53 | TEST(Pythagorean, simpleTest) 54 | { 55 | // arrange 56 | mock().expectOneCall("SquareRoot_sqrt")// expect Pythagorean_hypotenuse(...) call SquareRoot_sqrt(...). 57 | .withParameter("number", 25.0) // bubble 2 - expect the number is passed as 25.0, 58 | // because 3.0^2 + 4.0^2 is 25.0. 59 | .andReturnValue(5.0f); // bubble 3 - return the result as 5.0f, 60 | // because square root of 25.0 is 5.0. 61 | 62 | // act 63 | float hypotenuse = Pythagorean_hypotenuse(3.0, 4.0); // bubble 3, 4 64 | 65 | // assert 66 | mock().checkExpectations(); 67 | CHECK_EQUAL(5.0f, hypotenuse); 68 | } 69 | ``` 70 | 71 | ### Test 2 - Black Box Test 72 | 73 | ```C 74 | TEST(Pythagorean, BlackBoxTest) 75 | { 76 | // arrange 77 | mock().expectOneCall("SquareRoot_sqrt")// expect Pythagorean_hypotenuse(...) call SquareRoot_sqrt(...). 78 | .withParameter("number", 100.0) // bubble 2 - expect the number is passed as 100. 79 | // because 6.0^2 + 8.0^2 is 100.0. 80 | .andReturnValue(100.0f); // bubble 3 - intentionally set the result as 100.0 instead of 10.0. 81 | // we want to assure Pythagorean_hypotenuse(x, y) returns whatever SquareRoot_sqrt(number)` gives. 82 | 83 | // act 84 | float hypotenuse = Pythagorean_hypotenuse(6.0, 8.0); // bubble 3, 4 85 | 86 | // assert 87 | mock().checkExpectations(); 88 | CHECK_EQUAL(100.0f, hypotenuse); 89 | } 90 | ``` 91 | 92 | ### Building the example on Linux 93 | 94 | ``` 95 | ~/cpputest_example (master)$ cmake -B build -G "Unix Makefiles" 96 | -- The C compiler identification is GNU 7.5.0 97 | -- The CXX compiler identification is GNU 7.5.0 98 | -- Check for working C compiler: /usr/bin/cc 99 | -- Check for working C compiler: /usr/bin/cc -- works 100 | -- Detecting C compiler ABI info 101 | -- Detecting C compiler ABI info - done 102 | -- Detecting C compile features 103 | -- Detecting C compile features - done 104 | -- Check for working CXX compiler: /usr/bin/c++ 105 | -- Check for working CXX compiler: /usr/bin/c++ -- works 106 | -- Detecting CXX compiler ABI info 107 | -- Detecting CXX compiler ABI info - done 108 | -- Detecting CXX compile features 109 | -- Detecting CXX compile features - done 110 | -- Configuring done 111 | -- Generating done 112 | -- Build files have been written to: /home/max/cpputest_example/build 113 | ~/cpputest_example (master)$ ls 114 | CMakeLists.txt LICENSE build readme.md src tests 115 | ~/cpputest_example (master)$ cd build 116 | ~/cpputest_example/build (master)$ make 117 | Scanning dependencies of target CppUTest 118 | ... 119 | ... 120 | ... 121 | [ 93%] Building C object src/CMakeFiles/cpputest_example_pythagorean.dir/main.c.o 122 | [ 95%] Building C object src/CMakeFiles/cpputest_example_pythagorean.dir/pythagorean.c.o 123 | [ 97%] Building C object src/CMakeFiles/cpputest_example_pythagorean.dir/square_root.c.o 124 | [100%] Linking C executable cpputest_example_pythagorean 125 | [100%] Built target cpputest_example_pythagorean 126 | ``` 127 | 128 | ### Test Result 129 | 130 | ```text 131 | ~/cpputest_example/build $ ctest -V 132 | ... 133 | test 1 134 | Start 1: pythagorean_test 135 | 136 | 1: Test command: 137 | ~/cpputest_example/code-build/tests/pythagorean_ut/pythagorean_ut.exe 138 | 1: Test timeout computed to be: 1500 139 | 1: 140 | 1: SquareRoot_sqrt gets called with parameter: 100.000000. 141 | 1: . 142 | 1: SquareRoot_sqrt gets called with parameter: 25.000000. 143 | 1: . 144 | 1: OK (2 tests, 2 ran, 6 checks, 0 ignored, 0 filtered out, 0 ms) 145 | 1: 146 | 1/1 Test #1: pythagorean_test ................. Passed 0.09 sec 147 | 148 | 100% tests passed, 0 tests failed out of 1 149 | 150 | Total Test time (real) = 0.15 sec 151 | ``` 152 | 153 | ## References 154 | 155 | 1. [CppUTest](http://cpputest.github.io/manual.html) 156 | 2. [CppUMock](http://cpputest.github.io/mocking_manual.html) 157 | 3. [CMake Tutorial - JohnLamp.netJohnLamp.net](https://www.johnlamp.net/cmake-tutorial.html) 158 | 4. [CMake Tutorial | CMake](https://cmake.org/cmake-tutorial/) 159 | 5. [Introduction to CMake by Example | derekmolloy.ie](http://derekmolloy.ie/hello-world-introductions-to-cmake/) 160 | -------------------------------------------------------------------------------- /docs/unit_test.uxf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 4 | 5 | UMLClass 6 | 7 | 360 8 | 230 9 | 100 10 | 30 11 | 12 | :Pythogorean 13 | bg=red 14 | fg=black 15 | 16 | 17 | 18 | UMLClass 19 | 20 | 590 21 | 230 22 | 100 23 | 30 24 | 25 | :SquareRoot 26 | bg=yellow 27 | 28 | 29 | 30 | UMLSpecialState 31 | 32 | 160 33 | 290 34 | 20 35 | 20 36 | 37 | type=initial 38 | 39 | 40 | 41 | Relation 42 | 43 | 630 44 | 250 45 | 30 46 | 160 47 | 48 | lt=. 49 | 10.0;10.0;10.0;140.0 50 | 51 | 52 | Relation 53 | 54 | 400 55 | 250 56 | 30 57 | 160 58 | 59 | lt=. 60 | layer=0 61 | 10.0;10.0;10.0;140.0 62 | 63 | 64 | Relation 65 | 66 | 170 67 | 280 68 | 250 69 | 40 70 | 71 | lt=->>> 72 | Pythagorean_hypotenuse(x, y) 73 | 10.0;20.0;230.0;20.0 74 | 75 | 76 | UMLGeneric 77 | 78 | 400 79 | 300 80 | 20 81 | 70 82 | 83 | 84 | bg=red 85 | 86 | 87 | 88 | Relation 89 | 90 | 410 91 | 300 92 | 240 93 | 40 94 | 95 | lt=->>> 96 | SquareRoot_sqrt(number) 97 | 10.0;20.0;220.0;20.0 98 | 99 | 100 | UMLGeneric 101 | 102 | 630 103 | 320 104 | 20 105 | 40 106 | 107 | 108 | bg=yellow 109 | 110 | 111 | 112 | Relation 113 | 114 | 410 115 | 340 116 | 240 117 | 40 118 | 119 | lt=<<<. 120 | result 121 | 10.0;20.0;220.0;20.0 122 | 123 | 124 | Relation 125 | 126 | 150 127 | 350 128 | 270 129 | 40 130 | 131 | lt=<<<. 132 | result 133 | 10.0;20.0;250.0;20.0 134 | 135 | 136 | UMLUseCase 137 | 138 | 260 139 | 250 140 | 30 141 | 30 142 | 143 | *1* 144 | bg=blue 145 | 146 | 147 | 148 | UMLUseCase 149 | 150 | 490 151 | 270 152 | 30 153 | 30 154 | 155 | *2* 156 | bg=blue 157 | 158 | 159 | 160 | UMLUseCase 161 | 162 | 580 163 | 330 164 | 30 165 | 30 166 | 167 | *3* 168 | bg=blue 169 | 170 | 171 | 172 | UMLUseCase 173 | 174 | 350 175 | 340 176 | 30 177 | 30 178 | 179 | *4* 180 | bg=blue 181 | 182 | 183 | 184 | Relation 185 | 186 | 700 187 | 130 188 | 30 189 | 320 190 | 191 | lt=.. 192 | 10.0;300.0;10.0;10.0 193 | 194 | 195 | Relation 196 | 197 | 330 198 | 130 199 | 30 200 | 310 201 | 202 | lt=.. 203 | 10.0;290.0;10.0;10.0 204 | 205 | 206 | Relation 207 | 208 | 520 209 | 140 210 | 30 211 | 310 212 | 213 | lt=.. 214 | 10.0;290.0;10.0;10.0 215 | 216 | 217 | Text 218 | 219 | 570 220 | 140 221 | 100 222 | 30 223 | 224 | *Dependency* 225 | style=wordwrap 226 | 227 | 228 | 229 | Text 230 | 231 | 360 232 | 140 233 | 140 234 | 40 235 | 236 | *System Under Test* 237 | style=wordwrap 238 | 239 | 240 | 241 | --------------------------------------------------------------------------------