19 апр. 2007 г.

CppUnit для ленивых

Приспичило мне однажды овладеть юнит-тестированием кода C++. Так приспичило, что сил никаких нет - ни есть не могу, ни спать. Прошвырнувшись по ближайшим форумам, я понял, что не один такой, и что страждущих немало, но все они в каких-то непонятках. Казалось бы - чего тут сложного? Ан нет, каждый свое защищает: линуксоиды все как один говорят, что "у них все есть, но виндузятникам этого не понять", виндузятники раскидывают по сторонам слюни, описывая некую приблуду к Visual Studio, а все остальные тихо сидят и посмеиваются. В общем, черт их разберет, кто прав. Единственное название, упоминающееся более-менее часто - это CppUnit.
Вспомнив, как классно было работать с NUnit, я глядел на прогресс-бар скачивания дистрибутива, счастливо улыбаясь и предвкушая легкую жизнь. Но суровая реальность оказалась жестокой - ничего напоминающего мои прошлые опыты с инструментами тестирования я там не нашел.
Началось все с того, что меня заставили компилировать библиотеки самостоятельно. Но то ли авторы не удосужились проверить свое творение на последних версиях Студии, то ли Студия мне попалась кривая, но компиляция у меня шла, спотыкаясь на каждом шагу и ругаясь на чем свет стоит. В результате я все же смог получить нужные либы, правда, только для режима отладки.
Следующим шагом было прочтение readme.txt, который по совместительству является этаким сборником рецептов. Следуя инструкциям, я пытался заставить свои тесты запускаться с помощью GUI-утилиты TestPlugInRunnerd.exe, но потратив на это несколько часов, убедил себя, что GUI - это для маленьких, а настоящие дядьки работают в консоли, на чем и успокоился.
Запустить тесты в консоли оказалось не в пример проще. Ниже приведу последовательность действий, актуальную для Visual Studio 8 (2005).
1) Добавляем каталог include установленного CppUnit в стандартные пути (Tools - Options - Projects and Solutions - VC++ Directories).
2) Добавляем в проект ссылку на cppunitd.lib (в свойствах проекта: Configuration Properties - Linker - Input).
3) Пишем тест примерно такого вида:
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>

class MyTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(MyTest);
CPPUNIT_TEST(myTest1);
CPPUNIT_TEST(myTest2);
CPPUNIT_TEST(myTest3);
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
// инициализация
}
void tearDown() {
// деинициализация
}
void myTest1() {
CPPUNIT_ASSERT(true);
}
void myTest2() {
CPPUNIT_ASSERT(true);
}
void myTest3() {
CPPUNIT_ASSERT(false &&amp; true);
}
};

4) Пишем функцию main() примерно так:
#include <cppunit/ui/text/TestRunner.h>
#include "MyTest.h"

CPPUNIT_TEST_SUITE_REGISTRATION(MyTest);

int main()
{
CppUnit::TextUi::TestRunner runner;
CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest( registry.makeTest() );
runner.run();
return 0;
}

5) Запускаем и радуемся. Если появляются новые тесты, то для каждого из них (я имею в виду для каждого Test Suite) нужно добавить строку вида CPPUNIT_TEST_SUITE_REGISTRATION(...) в файл с функцией main(). После запуска программа выдаст на консоль информацию о выполненных тестах, и если какие-то из них не прошли, укажет причину ошибок и соответствующие номера строк в исходных файлах.

Вот так, дешево и сердито. Если кто знает, как делать то же самое, но с помощью GUI-утилиты, буду рад у вас сонно поучиться.