Writing your first test
In this example, we will create a simple calculator in C++ using Cest to test its behavior. To make things easy, we will run the build commands directly in the terminal, without using a build system. In the next tutorial, we will see how to integrate Cest with CMake.
Setup
First, let's create an empty directory where we will keep all the project's files. Then, the subdirectories to keep the source and build results of the project.
The resulting directory structure will be like the following:
Now, let's create the application's source files. Let's start by creating a main.cpp
file to
hold the application's entrypoint, and a calculator.cpp
file to hold the application's business logic.
We will start by performing the sum of 2+2, but let's keep the implementation empty first. We will fill in the details after writing its test. That will ensure the calculation is correct.
#include <iostream>
#include "calculator.h"
int main(int argc, char *argv[]) {
std::cout << "2+2 = " << Sum(2, 2) << std::endl;
return 0;
}
After creating these files, the directory structure will look like this:
/calculator
├ /src
│ ├ main.cpp
│ ├ calculator.cpp
│ └ calculator.h
├ /test
├ /lib
└ /build
Finally, let's install the latest version of Cest Framework. You can get the latest version from GitHub, or directly run in the terminal:
cd lib
wget https://github.com/cegonse/cest/releases/download/v2/cest
cd ..
After these commands finish, we will have the full setup in place:
/calculator
├ /src
│ ├ main.cpp
│ ├ calculator.cpp
│ └ calculator.h
├ /test
├ /lib
│ └ cest
└ /build
Building the application
To build the application, let's invoke the C++ compiler and build all the project's source files into
a single binary. We will assume the GNU Compiler is installed, invoking g++
. If you are using macOS
and have installed the XCode Build Tools, you will probably have CLang installed instead. In that case,
run clang++
.
If something went wrong during the compilation process, go to the previous step and check whether you are
missing any file or you have any errors in the source files. Otherwise, the calculator program will be placed
inside the /build
directory:
/calculator
├ /src
│ ├ main.cpp
│ ├ calculator.cpp
│ └ calculator.h
├ /test
├ /lib
│ └ cest
└ /build
└ calculator
Running the application will show the calculation result, which is wrong:
Writing our first test
Now we have a calculator application that builds and runs, but the business logic is not implemented yet. Let's build its logic through a test, ensuring the behavior is correct.
First, let's create the test file for the calculator.cpp
file. Check the annotations () to know
more about each section of the test file.
#include <cest>
#include <calculator.h>
describe("Calculator", []() { // (1)
describe("sum()", []() { // (2)
it("adds two numbers", []() {
auto a = 2, b = 2;
auto r = Sum(a, b);
expect(r).toEqual(4); // (3)
});
it("supports negative numbers", []() {
auto a = -2, b = 4;
auto r = Sum(a, b);
expect(r).toEqual(2);
});
});
});
describe
blocks define test-suites. Each test suite may have any number of tests.- Test-suites can be nested one inside each other. Cest will run them in outside-in order.
expect()
is the keyword that lets you do assertions on the calculation results of your code. The whole set of available assertions is available in the API reference
With our test in place, let's build and run the calculator test. The test can be easily run by executing the compiled file, as Cest tests are stand-alone executable applications. Check out the different output styles in the API reference API reference or by running the test with the --help
flag.
g++ -Ilib -Isrc src/calculator.cpp test/calculator.test.cpp -o build/calculator_test
After compiling the test, the directory structure will look like this:
/calculator
├ /src
│ ├ main.cpp
│ ├ calculator.cpp
│ └ calculator.h
├ /test
│ └ calculator.test.cpp
├ /lib
│ └ cest
└ /build
├ calculator_test
└ calculator
Finally, let's run the test and see its output. The output may look bare in this snippet, but you will see results with full color and rich text in the terminal.
./build/calculator_test
FAIL test/calculator.test.cpp:6 adds two numbers
Failed at line 9: Expected 4, was 0
FAIL test/calculator.test.cpp:12 supports negative numbers
Failed at line 15: Expected 2, was 0
We can see both tests have failed, as expected. Let's correct the implementation of our business logic to ensure the tests pass:
And now, when running the tests:
g++ -Ilib -Isrc src/calculator.cpp test/calculator.test.cpp -o build/calculator_test
./build/calculator_test
PASS test/calculator.test.cpp:6 adds two numbers
PASS test/calculator.test.cpp:12 supports negative numbers
All tests have passed! If we try compiling and running the application again, we will now see the correct result on the terminal:
g++ src/main.cpp src/calculator.cpp -o build/calculator
./build/calculator
2+2 = 4
Next reading
With this simple example, we have seen how does Cest Framework work, how to create a simple test, and how to integrate it with an application.
Next, we will learn how to integrate Cest Runner to easily manage our test suite.