202 lines
6.3 KiB
Markdown
202 lines
6.3 KiB
Markdown
# Tutorial 1.2: Introduction to Unit Testing
|
|
|
|
**Objective:** To introduce unit testing to the C++ project from Tutorial 1.1. This tutorial will cover adding a testing framework (`googletest`), creating a simple class, writing tests for that class, and integrating the tests into the CMake build system for easy execution within VSCode.
|
|
|
|
---
|
|
|
|
## 1. Prerequisites
|
|
|
|
* A completed and working project from "Tutorial 1.1: Your First Modern C++ Project".
|
|
* `vcpkg` installed and configured as per Tutorial 1.1.
|
|
|
|
---
|
|
|
|
## 2. Installing the Testing Framework
|
|
|
|
We will use `googletest`, a popular and powerful testing framework for C++.
|
|
|
|
Just like with `spdlog`, we use `vcpkg` to install it.
|
|
|
|
```bash
|
|
# Use the vcpkg executable to install the library
|
|
# Replace /path/to/vcpkg with your actual vcpkg path
|
|
/home/YOUR_USERNAME/dev/tools/vcpkg/vcpkg install gtest
|
|
```
|
|
*Note: The package name is `gtest` for `googletest`.*
|
|
|
|
---
|
|
|
|
## 3. Refactoring the Project Structure
|
|
|
|
To keep our project organized, we'll separate our main application code from the test code. The new structure inside `tutorial-1` will look like this:
|
|
```
|
|
tutorial-1/
|
|
├── CMakeLists.txt
|
|
├── src/
|
|
│ ├── calculator.h
|
|
│ ├── calculator.cpp
|
|
│ └── main.cpp
|
|
└── test/
|
|
└── test_calculator.cpp
|
|
```
|
|
The previous steps in this session have already created this structure for you.
|
|
|
|
---
|
|
|
|
## 4. Creating a Class to Test
|
|
|
|
We need some logic to test. We've created a simple `Calculator` class.
|
|
|
|
**File:** `tutorial-1/src/calculator.h`
|
|
```cpp
|
|
#ifndef CALCULATOR_H
|
|
#define CALCULATOR_H
|
|
|
|
class Calculator {
|
|
public:
|
|
int add(int a, int b);
|
|
};
|
|
|
|
#endif // CALCULATOR_H
|
|
```
|
|
|
|
**File:** `tutorial-1/src/calculator.cpp`
|
|
```cpp
|
|
#include "calculator.h"
|
|
|
|
int Calculator::add(int a, int b) {
|
|
return a + b;
|
|
}
|
|
```
|
|
|
|
We also updated `main.cpp` to use this new class.
|
|
|
|
**File:** `tutorial-1/src/main.cpp` (Updated)
|
|
```cpp
|
|
#include <iostream>
|
|
#include "spdlog/spdlog.h"
|
|
#include "calculator.h"
|
|
|
|
int main() {
|
|
spdlog::info("Hello, Modern C++! Initializing trading strategy...");
|
|
|
|
Calculator calc;
|
|
int result = calc.add(40, 2);
|
|
|
|
spdlog::info("The result of 40 + 2 is: {}", result);
|
|
|
|
std::cout << "Check your terminal for spdlog output!" << std::endl;
|
|
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Writing the Unit Tests
|
|
|
|
Now, we'll write our first test in the `test` directory.
|
|
|
|
**File:** `tutorial-1/test/test_calculator.cpp`
|
|
```cpp
|
|
#include <gtest/gtest.h>
|
|
#include "../src/calculator.h" // Include the header for the class we are testing
|
|
|
|
// Test fixture for Calculator class
|
|
class CalculatorTest : public ::testing::Test {
|
|
protected:
|
|
Calculator calc;
|
|
};
|
|
|
|
// Test case for the add method
|
|
TEST_F(CalculatorTest, Add) {
|
|
EXPECT_EQ(calc.add(2, 2), 4);
|
|
EXPECT_EQ(calc.add(-1, 1), 0);
|
|
EXPECT_EQ(calc.add(-5, -5), -10);
|
|
}
|
|
|
|
// Another test case for the add method for good measure
|
|
TEST_F(CalculatorTest, AddWithZero) {
|
|
EXPECT_EQ(calc.add(0, 5), 5);
|
|
EXPECT_EQ(calc.add(5, 0), 5);
|
|
EXPECT_EQ(calc.add(0, 0), 0);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Updating the Build System (CMake)
|
|
|
|
This is the most critical part. We have updated our `CMakeLists.txt` to build both our main application and our tests as separate executables.
|
|
|
|
**File:** `tutorial-1/CMakeLists.txt` (Updated)
|
|
```cmake
|
|
cmake_minimum_required(VERSION 3.15)
|
|
project(Tutorial1 LANGUAGES CXX)
|
|
|
|
set(CMAKE_CXX_STANDARD 17)
|
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
|
|
# Find dependencies
|
|
find_package(spdlog CONFIG REQUIRED)
|
|
find_package(GTest CONFIG REQUIRED)
|
|
|
|
# Create a library for our application code
|
|
# This allows us to share the code between the main app and the tests
|
|
add_library(MyLib src/calculator.cpp src/calculator.h)
|
|
target_include_directories(MyLib PUBLIC src)
|
|
|
|
# Define our main executable
|
|
add_executable(App src/main.cpp)
|
|
|
|
# Link our App against spdlog and our library
|
|
target_link_libraries(App PRIVATE spdlog::spdlog MyLib)
|
|
|
|
# Enable testing with CTest (CMake's testing framework)
|
|
enable_testing()
|
|
|
|
# Define the test executable
|
|
add_executable(RunTests test/test_calculator.cpp)
|
|
|
|
# Link our test executable against GTest and our library
|
|
target_link_libraries(RunTests PRIVATE GTest::GTest GTest::Main MyLib)
|
|
|
|
# Add the test to CTest
|
|
include(GoogleTest)
|
|
gtest_add_tests(TARGET RunTests)
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Building and Running Tests in VSCode
|
|
|
|
1. **Install `gtest`:** Make sure you have installed `gtest` using `vcpkg` as described in step 2.
|
|
2. **Configure CMake:** Run `CMake: Configure` from the command palette (Ctrl+Shift+P). It should detect the changes and find `gtest`.
|
|
3. **Build:** Click the **Build** button in the status bar. This will now build `App` and `RunTests`.
|
|
4. **Run Tests:** The "CMake Tools" extension provides a "Test Explorer" view in VSCode.
|
|
* Open the Test Explorer from the side bar (the icon looks like a beaker).
|
|
* You should see your test suite (`CalculatorTest`) and the individual tests.
|
|
* Click the "play" button at the top of the Test Explorer to run all tests.
|
|
* You can also run individual tests. The results will be shown directly in the UI.
|
|
|
|
---
|
|
|
|
**Congratulations!** You have now added unit testing to your project. This is a crucial skill for writing robust and maintainable C++ applications. You've learned how to:
|
|
* Add a testing library (`googletest`) with `vcpkg`.
|
|
* Structure your project to separate app code from test code.
|
|
* Create a shared library and multiple executables with CMake.
|
|
* Write and run unit tests using the VSCode Test Explorer.
|
|
|
|
|
|
## Feedback and Questions
|
|
|
|
1. The line ```target_link_libraries(RunTests PRIVATE GTest::GTest GTest::Main MyLib)``` caused cmake compilation error, I changed to
|
|
```target_link_libraries(RunTests PRIVATE GTest::gtest_main MyLib)```
|
|
I looked up documentation for this.
|
|
|
|
2. Are there alternative ways to install dependencies other the through vcpkg? For example would FetchContent work (https://cmake.org/cmake/help/latest/module/FetchContent.html)?
|
|
|
|
3.Interestingly, in vscode, the line ```#include <gtest/gtest.h>``` i was curious where the files as located - as i had not specified where it is other the download gtest in a separate process via vcpkg. when in vscode i hovered and clicked on go to definition it went to the location. curious how vscode knows this information.
|
|
|
|
4.I was interested to debug unit tests individually and managed to do this by following https://github.com/microsoft/vscode-cmake-tools/blob/main/docs/debug-launch.md#debugging-tests
|