Совсем недавно в ходе разработки одного проекта передо мной встала задача поиска удобного средства для написания юнит-тестов на C++. К каким результатам привело моё исследование подробнее в этой статье.
Прежде, чем приступать к конкретным средствам создания юнит тестов, давайте определимся, что такое юнит- (или модульное) тестирование, его основные принципы и зачем, вообще, нужна отдельная библиотека под это дело.
Итак, данный принцип заключается в написании небольших тестов для каждой нетривиальной функции или метода исходного кода нашей программы в отдельности. Что нам даёт такой подход? Во- первых, после проведения такого тестирования, мы точно можем сказать, что по отдельности всё части работоспособны (не стоит путать с понятием «отсутствуют ошибки»); во- вторых, мы можем проводить регрессионное тестирование. Ну и в- третьих, юнит- тест является хорошим примером того, как нужно использовать тестируемые функции и классы, т.е. модульный тест – средство косвенной документации.
Также, хотелось бы затронуть основные, на мой взгляд, принципы модульного тестирования. Перечислим их:
В результате нам нужно средство, которое автоматизирует различные рутинные операции (типа, поддержки main(), создания экземпляров классов, вывода сообщений и др.). Выдвинем несколько требований:
На данный момент существует огромное количество (как говориться, over 9000) библиотек для написания модульных тестов под C++. Тут для каждого найдётся своё средство по нраву. Коротко перечислю несколько из них.
Boost::Test
Boost — одна из самых известных (и сам больших) библиотек для C++, а Boost::Test – это Фреймворк для тестирования, входящий в неё и построенный на макросах.
Первое моё знакомство с Boost оставило не очень хорошие воспоминания – слишком громоздкая и неуклюжая, на мой взгляд, документация, к тому же весит этот монстр довольно много (да, всю библиотеку прикреплять к проекту не надо, можно использовать Boost::Test, но качать придётся целиком).
Приведу небольшой пример теста из документации:
QTestLib
Также не менее известная библиотека, но по отзывам сложновата, да и тянуть всё QT ради тестов не оправдано.
UnitTest++
Довольно не плохая и популярная вещь, но бедновата документация.
Ну и наконец:
CxxTest
На мой взгляд одна из лучших xUnit- подобных библиотек для написания модульных тестов.
Из особенностей:
Из недостатков:
А теперь посмотрим, как работать с CxxTest
В случае успеха получим выражение, вида:
Ну а если, что-то не ладно в нашем исходном коде, то CxxTest нам вежливо укажет:
CxxTest довольно удобный каркас для написания модульных тестов на C++, кроме того он не только облегчает работу программисту, но и способствует правильному написанию тестов.
По мимо перечисленных особенностей в CxxTest можно элементарно сделать простенький интерфейс для визуализации тестов (в виде прогресс- бара), а также есть поддержка Mock- объектов.
P.S.
Большое спасибо Inversion за приглашение.
Прежде, чем приступать к конкретным средствам создания юнит тестов, давайте определимся, что такое юнит- (или модульное) тестирование, его основные принципы и зачем, вообще, нужна отдельная библиотека под это дело.
Итак, данный принцип заключается в написании небольших тестов для каждой нетривиальной функции или метода исходного кода нашей программы в отдельности. Что нам даёт такой подход? Во- первых, после проведения такого тестирования, мы точно можем сказать, что по отдельности всё части работоспособны (не стоит путать с понятием «отсутствуют ошибки»); во- вторых, мы можем проводить регрессионное тестирование. Ну и в- третьих, юнит- тест является хорошим примером того, как нужно использовать тестируемые функции и классы, т.е. модульный тест – средство косвенной документации.
Также, хотелось бы затронуть основные, на мой взгляд, принципы модульного тестирования. Перечислим их:
- Тесты должны быть маленькими и быстрыми.
- Тесты должны быть автоматическими.
- Тесты не должны зависеть друг от друга и от порядка их следования.
- Сначала пишем тесты в режиме «чёрного» ящика.
- А затем, в режиме «белого».
- Нашли ошибку – хороший повод для написания теста, воспроизводящую её.
- Структура тестов должна повторять иерархию проекта.
- Для каждого вашего класса заводите отдельный тестовый класс.
- Отделяйте тесты от основного проекта.
- Имейте единую систему для именования тестов (к примеру, для класса MyClass тестирующий класс будет носить имя MyClassTest, а все его методы иметь вид testЧтоТестируем()).
В результате нам нужно средство, которое автоматизирует различные рутинные операции (типа, поддержки main(), создания экземпляров классов, вывода сообщений и др.). Выдвинем несколько требований:
- Средство должно быть простым в запуске, поддержке и освоении.
- Должно быть мощным.
- Должно быть кросплатформенным.
- Хорошо, если средство задаёт некоторый каркас построения тестов.
На данный момент существует огромное количество (как говориться, over 9000) библиотек для написания модульных тестов под C++. Тут для каждого найдётся своё средство по нраву. Коротко перечислю несколько из них.
Boost::Test
Boost — одна из самых известных (и сам больших) библиотек для C++, а Boost::Test – это Фреймворк для тестирования, входящий в неё и построенный на макросах.
Первое моё знакомство с Boost оставило не очень хорошие воспоминания – слишком громоздкая и неуклюжая, на мой взгляд, документация, к тому же весит этот монстр довольно много (да, всю библиотеку прикреплять к проекту не надо, можно использовать Boost::Test, но качать придётся целиком).
Приведу небольшой пример теста из документации:
Copy Source | Copy HTML
- #include <boost/test/minimal.hpp>
-
- Int add( int i, int j )
- {
- return i+j;
- }
-
- Int test_main( int, char *[ ] ) // note the name!
- {
- BOOST_CHECK( add( 2,2 ) == 4 ); // #1 continues on error
- BOOST_REQUIRE( add( 2,2 ) == 4 ); // #2 throws on error
-
- if( add( 2,2 ) != 4 )
- BOOST_ERROR( "Ouch..." ); // #3 continues on error
- if( add( 2,2 ) != 4 )
- BOOST_FAIL( "Ouch..." ); // #4 throws on error
- if( add( 2,2 ) != 4 )
- throw "Oops..."; // #5 throws on error
-
- return add( 2, 2 ) == 4 ? 0 : 1; // #6 returns error code
- }
QTestLib
Также не менее известная библиотека, но по отзывам сложновата, да и тянуть всё QT ради тестов не оправдано.
UnitTest++
Довольно не плохая и популярная вещь, но бедновата документация.
Ну и наконец:
CxxTest
На мой взгляд одна из лучших xUnit- подобных библиотек для написания модульных тестов.
Из особенностей:
- Не нужно ничего встраивать в исходный код
- Не требует установки (можно легко встроить в ваш проект в ветку «test»)
- Не тянет за собой каких- либо сторонних библиотек
- Имеет одно из лучших, на мой взгляд, руководств. Обучение идёт от простого к сложному (с различными примерами и картинками;) ).
Из недостатков:
- Нужен Perl или Python. Собственно, на Linux’ах есть оба по умолчанию, для Windows Python я использую как неплохое служебное средство.
А теперь посмотрим, как работать с CxxTest
- Создаём *.h файл
- Создаём класс, наследованный от CxxTest::TestSuite
- Реализуем методы testMethod(void) (ВАЖНО: первое слово должно быть обязательно test – иначе система не признает, как тест)
Copy Source | Copy HTML
- class MyTest : public CxxTest::TestSuite
- {
- public:
- void testMethod( void )
- {
- TS_ASSERT( 1 + 1 > 1 );
- TS_ASSERT_EQUALS( 1 + 1, 2 );
- }
- };
- Запускаем препроцессор
Copy Source | Copy HTML
- # perl cxxtestgen.pl --error-printer -o runner.cpp MyTest.h
- MyTest.h - написанный нами тест
- runner.cpp - выходной файл (в нём будет создана функция main())
- cxxtestgen.pl или cxxtestgen.py - препроцессор
- … И компилируем
Copy Source | Copy HTML
- # g++ -o runner runner.cpp
В случае успеха получим выражение, вида:
Copy Source | Copy HTML
- # ./runner
- Running 1 test.OK!
Ну а если, что-то не ладно в нашем исходном коде, то CxxTest нам вежливо укажет:
Copy Source | Copy HTML
- # ./runner
- Running 2 tests.
- MyTest.h :15: Expected (2 * 2 == 5), found (4 != 5)
- Failed 1 of 2 tests
- Success rate: 50%
Подведём итоги.
CxxTest довольно удобный каркас для написания модульных тестов на C++, кроме того он не только облегчает работу программисту, но и способствует правильному написанию тестов.
По мимо перечисленных особенностей в CxxTest можно элементарно сделать простенький интерфейс для визуализации тестов (в виде прогресс- бара), а также есть поддержка Mock- объектов.
P.S.
Большое спасибо Inversion за приглашение.