Pull to refresh

О модульном тестировании на C++ и о CxxTest

Reading time4 min
Views60K
Совсем недавно в ходе разработки одного проекта передо мной встала задача поиска удобного средства для написания юнит-тестов на C++. К каким результатам привело моё исследование подробнее в этой статье.


Прежде, чем приступать к конкретным средствам создания юнит тестов, давайте определимся, что такое юнит- (или модульное) тестирование, его основные принципы и зачем, вообще, нужна отдельная библиотека под это дело.

Итак, данный принцип заключается в написании небольших тестов для каждой нетривиальной функции или метода исходного кода нашей программы в отдельности. Что нам даёт такой подход? Во- первых, после проведения такого тестирования, мы точно можем сказать, что по отдельности всё части работоспособны (не стоит путать с понятием «отсутствуют ошибки»); во- вторых, мы можем проводить регрессионное тестирование. Ну и в- третьих, юнит- тест является хорошим примером того, как нужно использовать тестируемые функции и классы, т.е. модульный тест – средство косвенной документации.

Также, хотелось бы затронуть основные, на мой взгляд, принципы модульного тестирования. Перечислим их:
  • Тесты должны быть маленькими и быстрыми.
  • Тесты должны быть автоматическими.
  • Тесты не должны зависеть друг от друга и от порядка их следования.
  • Сначала пишем тесты в режиме «чёрного» ящика.
  • А затем, в режиме «белого».
  • Нашли ошибку – хороший повод для написания теста, воспроизводящую её.
  • Структура тестов должна повторять иерархию проекта.
  • Для каждого вашего класса заводите отдельный тестовый класс.
  • Отделяйте тесты от основного проекта.
  • Имейте единую систему для именования тестов (к примеру, для класса MyClass тестирующий класс будет носить имя MyClassTest, а все его методы иметь вид testЧтоТестируем()).

В результате нам нужно средство, которое автоматизирует различные рутинные операции (типа, поддержки main(), создания экземпляров классов, вывода сообщений и др.). Выдвинем несколько требований:
  • Средство должно быть простым в запуске, поддержке и освоении.
  • Должно быть мощным.
  • Должно быть кросплатформенным.
  • Хорошо, если средство задаёт некоторый каркас построения тестов.


На данный момент существует огромное количество (как говориться, over 9000) библиотек для написания модульных тестов под C++. Тут для каждого найдётся своё средство по нраву. Коротко перечислю несколько из них.

Boost::Test
Boost — одна из самых известных (и сам больших) библиотек для C++, а Boost::Test – это Фреймворк для тестирования, входящий в неё и построенный на макросах.
Первое моё знакомство с Boost оставило не очень хорошие воспоминания – слишком громоздкая и неуклюжая, на мой взгляд, документация, к тому же весит этот монстр довольно много (да, всю библиотеку прикреплять к проекту не надо, можно использовать Boost::Test, но качать придётся целиком).
Приведу небольшой пример теста из документации:

Copy Source | Copy HTML
  1. #include <boost/test/minimal.hpp>
  2.  
  3. Int add( int i, int j )
  4. {
  5.     return i+j;
  6. }
  7.  
  8. Int test_main( int, char *[ ] ) // note the name! 
  9. {
  10.     BOOST_CHECK( add( 2,2 ) == 4 ); // #1 continues on error 
  11.     BOOST_REQUIRE( add( 2,2 ) == 4 ); // #2 throws on error 
  12.  
  13.     if( add( 2,2 ) != 4 )
  14.         BOOST_ERROR( "Ouch..." ); // #3 continues on error 
  15.     if( add( 2,2 ) != 4 )
  16.         BOOST_FAIL( "Ouch..." ); // #4 throws on error 
  17.     if( add( 2,2 ) != 4 )
  18.         throw "Oops..."; // #5 throws on error 
  19.  
  20. return add( 2, 2 ) == 4 ?  0 : 1; // #6 returns error code 
  21. }


QTestLib
Также не менее известная библиотека, но по отзывам сложновата, да и тянуть всё QT ради тестов не оправдано.

UnitTest++
Довольно не плохая и популярная вещь, но бедновата документация.

Ну и наконец:
CxxTest
На мой взгляд одна из лучших xUnit- подобных библиотек для написания модульных тестов.
Из особенностей:
  • Не нужно ничего встраивать в исходный код
  • Не требует установки (можно легко встроить в ваш проект в ветку «test»)
  • Не тянет за собой каких- либо сторонних библиотек
  • Имеет одно из лучших, на мой взгляд, руководств. Обучение идёт от простого к сложному (с различными примерами и картинками;) ).

Из недостатков:
  • Нужен Perl или Python. Собственно, на Linux’ах есть оба по умолчанию, для Windows Python я использую как неплохое служебное средство.


А теперь посмотрим, как работать с CxxTest
  1. Создаём *.h файл
  2. Создаём класс, наследованный от CxxTest::TestSuite
  3. Реализуем методы testMethod(void) (ВАЖНО: первое слово должно быть обязательно test – иначе система не признает, как тест)
    Copy Source | Copy HTML
    1. class MyTest : public CxxTest::TestSuite
    2.     {
    3.     public:
    4.         void testMethod( void )
    5.         {
    6.             TS_ASSERT( 1 + 1 > 1 );
    7.             TS_ASSERT_EQUALS( 1 + 1, 2 );
    8.         }
    9.     };

  4. Запускаем препроцессор
    Copy Source | Copy HTML
    1. # perl cxxtestgen.pl --error-printer -o runner.cpp MyTest.h
    2.  
    3.     MyTest.h - написанный нами тест
    4.     runner.cpp - выходной файл (в нём будет создана функция main())
    5.     cxxtestgen.pl или cxxtestgen.py - препроцессор

  5. … И компилируем
    Copy Source | Copy HTML
    1. # g++ -o runner runner.cpp


В случае успеха получим выражение, вида:
Copy Source | Copy HTML
  1. # ./runner
  2. Running 1 test.OK!


Ну а если, что-то не ладно в нашем исходном коде, то CxxTest нам вежливо укажет:
Copy Source | Copy HTML
  1. # ./runner
  2. Running 2 tests.
  3. MyTest.h :15: Expected (2 * 2 == 5), found (4 != 5)
  4. Failed 1 of 2 tests
  5. Success rate: 50%


Подведём итоги.


CxxTest довольно удобный каркас для написания модульных тестов на C++, кроме того он не только облегчает работу программисту, но и способствует правильному написанию тестов.
По мимо перечисленных особенностей в CxxTest можно элементарно сделать простенький интерфейс для визуализации тестов (в виде прогресс- бара), а также есть поддержка Mock- объектов.

P.S.
Большое спасибо Inversion за приглашение.
Tags:
Hubs:
Total votes 20: ↑19 and ↓1+18
Comments29

Articles