Saturday, September 26, 2009

Boost Test. Настройка и использование

Недавно в своём блоге я делал краткий обзор инструментов юнит-тестирования и отмечал наличие короткой справки для быстрого старта с библиотекой UnitTest++. Сегодня я хочу предложить вариант такой справки для Boost Test, чего вы не найдёте в официальной документации к этому инструменту. Описание ориентировано на использование в Windows с Visual Studio 2005/2008.

Для работы с примерами вам нужно скачать с сайта www.boost.org пакет библиотек (на текущий момент версии 1.39). Распакуйте скачанный архив и мы можем начинать.

Сборка библиотеки Boost.Test

Использовать Boost.Test можно в бинарном виде и в виде подключаемого заголовочного файла. Далее я буду описывать использование этой библиотеки в двоичном виде, подключаемой к тестовому проекту статически.

В корневой директории скачанного дистрибутива Boost найдите файл bootstrap.bat и выполните его. В результате будет подготовлена к использованию утилита сборки Boost. В той же директории, где хранится только что запущенный bat-файл, появится файл bjam.exe.

Теперь запустите из командной строки blam.exe с параметрами сборки Boost.Test:
bjam --build-dir=..\boosttest\build_dir toolset=msvc --libdir=..\boosttest\lib --includedir=..\boosttest\include --with-test variant=debug link=static threading=multi runtime-link=shared install

В той же папке, куда вы распаковали архив Boost будет создана папка boosttest, содержащая 3 папки:
  • build_dir, где хранятся промежуточные и результирующие файлы сборки Boost.Test
  • include, с заголовочными файлами всех утилит библиотеки Boost для использования в вашем проекте
  • lib, с lib-файлами для подключения к проекту
Создание тестового проекта
Далее я опишу создание тестового проекта с юнит-тестами. Пользуясь этим описанием вы сможете по аналогии использовать тесты в своём проекте.

Добавлять юнит-тесты мы будем прямо в основной проект. Можно также юнит-тесты поместить в отдельный проект, но в этом случае у вас могут возникнуть проблемы линковки. Для варианта с отдельным проектом для тестов необходимо экспортировать тестируемые функции и классы, а это может усложнить работу с сильно связанными классами, возможно как раз теми, которым остро необходим рефакторинг.

Итак, создадим пустой проект:

New Project > Visual C++ > General > Empty Project

Сначала напишем простую программу умножающую 2 целых числа и отображающую их произведение.
Добавим в проект файл main.cpp со следующим содержанием:

//! main.cpp

#include <iostream>
#include "functions.h"

using namespace std;

void main()
{
cout << "Main Program" << endl;
cout << "5 * 4 = " << multiply(5, 4) << endl;
}
Также добавим файл с реализацией функции умножения:

//! functions.cpp
#include "functions.h"

int multiply(const int x, const int y)
{
return x * y;
}
И заголовочный файл с описанием функции умножения:

//! functions.h
int multiply(const int x, const int y);

Теперь добавим файлы с кодом тестирования. Сначала добавим файл maintest.cpp, в котором объявим константу BOOST_TEST_MAIN для того, чтобы Boost Test автоматически сгенерировал функцию main для тестов. Здесь же напишем первый тест, который всегда будет ошибочным. Я делаю это только для того, чтобы показать как использовать библиотеку для тестов, реализация которых находится в нескольких файлах.

//! maintest.cpp

#define BOOST_TEST_MAIN

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(SimpleTestInMainTestingModule)
{
BOOST_CHECK(1 == 2);
}

Теперь добавим ещё один файл functionsTest.cpp, который будет содержать тест для функции multiply из functions.cpp:

//! functionsTest.cpp

#include "functions.h"
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(Multiply)
{
BOOST_CHECK(multiply(4, 5) == 20);
}
Добавление тестовой конфигурации

Теперь необходимо решить проблему конфликта main, описанной в main.cpp и, той что будет сгенерирована Boost. Для этого добавим новую конфигурацию проекта для юнит-тестов, из сборки которой будет исключён модуль main.cpp, а в Debug и Release -конфигурациях нужно исключить из сборки mainTest.cpp

Чтобы добавить новую конфигурацию проекта в меню Build выберите Configuration Manager.... В появившемся диалоговом окне выберите в списке Active solution configuration пункт New. Введите имя новой конфигурации UnitTests и сохраните изменения.

Сделайте активной конфигурацию UnitTests. В контекстном меню файла main.cpp выберите Properties и на вкладке General для свойства Excluded from build установите значение Yes. Сделайте активным конфигурацию Debug и для файлов maintest.cpp и functionsTest.cpp также установите опцию исключения из сборки. Аналогично Debug настройте Release конфигурацию.

Теперь нужно настроить тестовую конфигурацию проекта. Снова сделайте активной конфигурацию UnitTests и в свойствах проекта для этой конфигурации выполните следующие настройки:

В настройках проекта C++ > General > Additional Include Directories вписать путь к заголовоным файлам boost.test

Linker > General > Additional Library Directories путь к папке с библиотекой libboost_unit_test_framework-vc90-mt-gd-1_39.lib

Linker > System > SubSystem выбрать из списка опцию Console (/SUBSYSTEM:CONSOLE)

Build Events > Post-Build Event > Command Line
"$(TargetDir)\$(TargetName).exe" --result_code=no --report_level=short

Build Events > Post-Build Event > Description
==== Run unit tests ====

Настроить вывод результатов можно иначе. Описание параметров, для управления выводом, приведено в официальной документации в разделе Runtime Parameters Reference.

Постройте и выполните проект в Debug или Release -конфигурациях. На экран будет выведено сообщение:

Main Program
5 * 4 = 20

Затем соберите проект в конфигурации UnitTests. В окне Output помимо информации о ходе компиляции будет что-то вроде этого:

==== Run unit tests ====
Running 2 test cases...
./maintest.cpp(13): error in "SimpleTestInMainTestingModule": check 1 == 2 failed
Test suite "Master Test Suite" failed with:
1 assertion out of 2 passed
1 assertion out of 2 failed
1 test case out of 2 passed
1 test case out of 2 failed

Если выполнить файл SingleProject.exe собранный в конфигурации UnitTests вы увидите примерно то же сообщение.
Если вы видите это сообщение, значит проект настроен правильно. Вы можете также скачать исходники проекта и поработать с ним.

Надеюсь эта информация будет полезна. Исправления и дополнения приветствуются!

10 comments:

Anonymous said...

Нужно поправить
Build Events > Post-Build Event > Command Line
$(TargetDir)\$(TargetName).exe --result_code=no --report_level=short

на
Build Events > Post-Build Event > Command Line
"$(TargetDir)\$(TargetName).exe" --result_code=no --report_level=short

Dmitry I Yastrebkov said...

Спасибо за подсказку.
Совершенно верно, если в пути есть пробелы нужно добавить кавычки

Anonymous said...

у меня ошибка линковки:
1>LINK : fatal error LNK1104: cannot open file 'libboost_unit_test_framework-vc90-mt-s-1_41.lib'
Boost не создает такого файла!

Dmitry I Yastrebkov said...

Тут всё просто.
Если вы собирали boost с параметрами, которые написал я, то обратите внимание на параметры сборки, там явно указано, что собираем мы Boost.Test в отладочной многопоточной версии для статической линковки.
То есть вам нужно либо в настройках проекта C/C++ -> Code Generation -> Runtime Library изменить /MT на /MDd, либо собрать Boost.Test с другими параметрами или вообще без указания варианта сборки (в этом случае будут собраны все варианты библиотек - и .lib и .dll)

Anonymous said...

Спасибо за ответ, помогло (коммент про fatal link error). Я новичок в Unit-тестировании, но очень нужно разобраться, причем срочно. А Boost.Test мне показалась слишком сложной для начинающего, но намерен долбать именно её. Не подскажите, где документация к библиотеке? Файл "units.pdf" вроде не то...
В любом случае Ваша статья мне очень помогла. Спасибо.

Dmitry I Yastrebkov said...

Вся документация прилагается к boost. Смотрите в архиве или по ссылке http://www.boost.org/doc/libs/1_39_0/libs/test/doc/html/index.html

sqborev said...

Как получить результат теста в виде XML?

Dmitry I Yastrebkov said...

В настройках проекта смотрите
Build Events > Post-Build Event > Command Line
В примере проекта там такая строчка
$(TargetDir)\$(TargetName).exe --result_code=no --report_level=short
Вам достаточно добавить еще один параметр --output_format=XML
А чтобы результат вывести в файл перенаправить поток вывода. Все вместе это будет выглядеть так:
$(TargetDir)\$(TargetName).exe --result_code=no --report_level=short --output_format=XML > UnitTests.log

Anonymous said...

у мен ошибка
fatal error C1083: Cannot open include file: 'boost/test/test_tools.hpp': No such file or directory
что делать???

Dmitry I Yastrebkov said...

В настройках проекта (C/C++ > General > Additional Include Directories) нужно добавить путь к директории boost