Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CPlusPlusNotesForProfessionals.pdf
Скачиваний:
47
Добавлен:
20.05.2023
Размер:
5.11 Mб
Скачать

Chapter 141: Unit Testing in C++

Unit testing is a level in software testing that validates the behavior and correctness of units of code.

In C++, "units of code" often refer to either classes, functions, or groups of either. Unit testing is often performed using specialized "testing frameworks" or "testing libraries" that often use non-trivial syntax or usage patterns.

This topic will review di erent strategies and unit testing libraries or frameworks.

Section 141.1: Google Test

Google Test is a C++ testing framework maintained by Google. It requires building the gtest library and linking it to your testing framework when building a test case file.

Minimal Example

// main.cpp

#include <gtest/gtest.h> #include <iostream>

//Google Test test cases are created using a C++ preprocessor macro

//Here, a "test suite" name and a specific "test name" are provided. TEST(module_name, test_name) {

std::cout << "Hello world!" << std::endl;

// Google Test will also provide macros for assertions. ASSERT_EQ(1+1, 2);

}

//Google Test can be run manually from the main() function

//or, it can be linked to the gtest_main library for an already

//set-up main() function primed to accept Google Test test cases. int main(int argc, char** argv) {

::testing::InitGoogleTest(&argc, argv);

return RUN_ALL_TESTS();

}

// Build command: g++ main.cpp -lgtest

Section 141.2: Catch

Catch is a header only library that allows you to use both TDD and BDD unit test style.

The following snippet is from the Catch documentation page at this link:

SCENARIO( "vectors can be sized and resized", "[vector]" ) { GIVEN( "A vector with some items" ) {

std::vector v( 5 );

REQUIRE( v.size() == 5 );

REQUIRE( v.capacity() >= 5 );

WHEN( "the size is increased" ) { v.resize( 10 );

THEN( "the size and capacity change" ) {

REQUIRE( v.size() == 10 );

GoalKicker.com – C++ Notes for Professionals

665

REQUIRE( v.capacity() >= 10 );

}

}

WHEN( "the size is reduced" ) { v.resize( 0 );

THEN( "the size changes but not capacity" ) { REQUIRE( v.size() == 0 );

REQUIRE( v.capacity() >= 5 );

}

}

WHEN( "more capacity is reserved" ) { v.reserve( 10 );

THEN( "the capacity changes but not the size" ) { REQUIRE( v.size() == 5 );

REQUIRE( v.capacity() >= 10 );

}

}

WHEN( "less capacity is reserved" ) { v.reserve( 0 );

THEN( "neither size nor capacity are changed" ) { REQUIRE( v.size() == 5 );

REQUIRE( v.capacity() >= 5 );

}

}

}

}

Conveniently, these tests will be reported as follows when run:

Scenario: vectors can be sized and resized Given: A vector with some items When: more capacity is reserved Then: the capacity changes but not the size

GoalKicker.com – C++ Notes for Professionals

666

Chapter 142: C++ Debugging and Debugprevention Tools & Techniques

A lot of time from C++ developers is spent debugging. This topic is meant to assist with this task and give inspiration for techniques. Don't expect an extensive list of issues and solutions fixed by the tools or a manual on the mentioned tools.

Section 142.1: Static analysis

Static analysis is the technique in which on checks the code for patterns linked to known bugs. Using this technique is less time consuming than a code review, though, its checks are only limited to those programmed in the tool.

Checks can include the incorrect semi-colon behind the if-statement (if (var);) till advanced graph algorithms which determine if a variable is not initialized.

Compiler warnings

Enabling static analysis is easy, the most simplistic version is already build-in in your compiler:

clang++-Wall -Weverything -Werror ...

g++-Wall -Weverything -Werror ...

cl.exe/W4 /WX ...

If you enable these options, you will notice that each compiler will find bugs the others don't and that you will get errors on techniques which might be valid or valid in a specific context. while (staticAtomicBool); might be acceptable even if while (localBool); ain't.

So unlike code review, you are fighting a tool which understands your code, tells you a lot of useful bugs and sometimes disagrees with you. In this last case, you might have to suppress the warning locally.

As the options above enable all warnings, they might enable warnings you don't want. (Why should your code be C++98 compatible?) If so, you can simply disable that specific warning:

clang++ -Wall -Weverything -Werror -Wno-errortoaccept ...

g++ -Wall -Weverything -Werror -Wno-errortoaccept ...

cl.exe /W4 /WX /wd<no of warning>...

Where compiler warnings assist you during development, they slow down compilation quite a bit. That is why you might not always want to enable them by default. Either you run them by default or you enable some continuous integration with the more expensive checks (or all of them).

External tools

If you decide to have some continuous integration, the use of other tools ain't such a stretch. A tool like clang-tidy has an list of checks which covers a wide range of issues, some examples:

Actual bugs

Prevention of slicing

Asserts with side e ects

Readability checks

Misleading indentation

Check identifier naming

Modernization checks

 

GoalKicker.com – C++ Notes for Professionals

667

Use make_unique()

Use nullptr

Performance checks

Find unneeded copies

Find ine cient algorithm calls

The list might not be that large, as Clang already has a lot of compiler warnings, however it will bring you one step closer to a high quality code base.

Other tools

Other tools with similar purpose exist, like:

the visual studio static analyzer as external tool clazy, a Clang compiler plugin for checking Qt code

Conclusion

A lot static analysis tools exist for C++, both build-in in the compiler as external tools. Trying them out doesn't take that much time for easy setups and they will find bugs you might miss in code review.

Section 142.2: Segfault analysis with GDB

Lets use the same code as above for this example.

#include <iostream>

void fail() { int *p1;

int *p2(NULL); int *p3 = p1; if (p3) {

std::cout << *p2 << std::endl;

}

}

int main() { fail();

}

First lets compile it

g++ -g -o main main.cpp

Lets run it with gdb

gdb ./main

Now we will be in gdb shell. Type run.

(gdb) run

The program being debugged has been started already. Start it from the beginning? (y or n) y

Starting program: /home/opencog/code-snippets/stackoverflow/a.out

Program received signal SIGSEGV, Segmentation fault. 0x0000000000400850 in fail () at debugging_with_gdb.cc:11

GoalKicker.com – C++ Notes for Professionals

668