Инструменты Open Source для модульного тестирования C/C++: Часть 1. Знакомство с фреймворком Boost

Каждый программный продукт требует выполнения регрессионных тестов. Традиционно фреймворки для модульного тестирования разрабатывались различными командами тестировщиков по мере необходимости, что не только усложняло поддержку тестовых комплексов, но и привязывало их определенные функции (например, мониторинга загрузки памяти) к конкретной операционной системе. Учитывая все эти проблемы, в этой серии статей представлены различные варианты по созданию эффективных фреймворков для регрессионного тестирования на базе программного обеспечения Open Source. Первая статья серии знакомит вас с Boost – фреймворком для модульного тестирования программных продуктов, написанных на языке C/C++.

Арпан Сен, технический директор, Synapti Computer Aided Design Pvt Ltd

Арпан Сен (Arpan Sen) – ведущий инженер, работающий над разработкой программного обеспечения в области автоматизации электронного проектирования. На протяжении нескольких лет он работал над некоторыми функциями UNIX, в том числе Solaris, SunOS, HP-UX и IRIX, а также Linux и Microsoft Windows. Он проявляет живой интерес к методикам оптимизации производительности программного обеспечения, теории графов и параллельным вычислениям. Арпан является аспирантов в области программных систем.



13.01.2012

Что такое модульное тестирование?

С большой долей вероятности сложный код, написанный на языке C/C++, содержит ошибки. После того, как код написан, найти эти ошибки будет также сложно, как найти иголку в стоге сена. Более разумный подход заключается в тестировании отдельных фрагментов кода по мере их создания. Для этого создаются небольшие (модульные) тесты для проверки конкретных параметров, например, некоторой функции на языке C, выполняющей интенсивные вычисления, или некоторого класса C++, требующего создания определенной структуры данных, таких как очередь. Комплекс регрессионных тестов, созданный в соответствии с этой философией, будет включать в себя набор модульных тестов и управляющую программу, которая будет запускать эти тесты и сообщать о результатах.


Создание теста для определенной функции или класса

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

Рассмотрим простой строковый класс, приведенный в листинге 1. Этот класс не очень надежен, поэтому мы протестируем его с помощью Boost.

Листинг 1. Обычный строковый класс
#ifndef _MYSTRING
#define _MYSTRING

class mystring { 
  char* buffer; 
  int length;
  public: 
    void setbuffer(char* s) { buffer = s; length = strlen(s); } 
    char& operator[ ] (const int index) { return buffer[index]; }
    int size( ) { return length; }
 }; 

#endif

Ряд типовых тестов проверяет, имеет ли пустая строка нулевую длину, не произошел ли выход за границы индекса, что может отображаться в сообщениях об ошибках или исключениях, и т. д. В листинге 2 приведены некоторые тесты, которые важно выполнять для любой строковой реализации. Чтобы выполнить код листинга 2, просто скомпилируйте его с помощью g++ (или любого другого компилятора, совместимого со стандартами C++). При этом нет необходимости как в обособлении главной функции, так и в использовании каких-либо библиотек компоновки: все необходимые определения включены в заголовок unit_test.hpp, который является частью установки Boost.

Листинг 2. Модульные тесты для строкового класса
#define BOOST_TEST_MODULE stringtest
#include <boost/test/included/unit_test.hpp>
#include "./str.h"

BOOST_AUTO_TEST_SUITE (stringtest) // name of the test suite is stringtest

BOOST_AUTO_TEST_CASE (test1)
{
  mystring s;
  BOOST_CHECK(s.size() == 0);
}

BOOST_AUTO_TEST_CASE (test2)
{
  mystring s;
  s.setbuffer("hello world");
  BOOST_REQUIRE_EQUAL ('h', s[0]); // basic test 
}

BOOST_AUTO_TEST_SUITE_END( )

Макросы BOOST_AUTO_TEST_SUITE и BOOST_AUTO_TEST_SUITE_END определяют начало и конец тестового пакета, соответственно. Отдельные тесты располагаются между этими макросами, и в этом плане их семантика подобна пространствам имен C++. Каждый отдельный модульный тест определяется с помощью макроса BOOST_AUTO_TEST_CASE. В листинге 3 показаны результаты выполнения кода листинга 2.

Листинг 3. Результаты выполнения кода листинга 2
[arpan@tintin] ./a.out
Running 2 test cases...
test.cpp(10): error in "test1": check s.size() == 0 failed

*** 1 failure detected in test suite "stringtest"

Давайте подробнее рассмотрим процесс создания модульных тестов из предыдущих листингов. Основная идея заключается в тестировании отдельных свойств класса с помощью макросов Boost. Макросы BOOST_CHECK и BOOST_REQUIRE_EQUAL являются предопределенными макросами (или инструментами тестирования), включенными в состав фреймворка Boost, и предназначены для проверки правильности выходных результатов кода.


Инструменты тестирования Boost

Boost содержит целый набор инструментов тестирования, являющихся в основном макросами для оценки выражений. Эти инструменты разделены на три основных категории: BOOST_WARN, BOOST_CHECK и BOOST_REQUIRE. Разница между категориями BOOST_CHECK и BOOST_REQUIRE заключается в том, что в первом случае выполнение теста продолжается даже при нарушении условия, тогда как во втором случае ошибка считается критической и выполнение теста прерывается. В листинге 4 используется простой фрагмент кода C++, демонстрирующий различия между категориями макросов.

Листинг 4. Три варианта использования инструментов тестирования Boost
#define BOOST_TEST_MODULE enumtest
#include <boost/test/included/unit_test.hpp>

BOOST_AUTO_TEST_SUITE (enum-test) 

BOOST_AUTO_TEST_CASE (test1)
{
  typedef enum {red = 8, blue, green = 1, yellow, black } color;
  color c = green;
  BOOST_WARN(sizeof(green) > sizeof(char));
  BOOST_CHECK(c == 2); 
  BOOST_REQUIRE(yellow > red); 
  BOOST_CHECK(black != 4);
}

BOOST_AUTO_TEST_SUITE_END( )

Первая проверка BOOST_CHECK завершилась неудачей, так же как и первая проверка BOOST_REQUIRE. Однако до второй проверки BOOST_CHECK дело не дошло, поскольку работа теста была прервана сразу же после неудачного завершения проверки BOOST_REQUIRE. В листинге 5 показаны результаты выполнения кода листинга 4.

Листинг 5. Понимание различий между категориями BOOST_REQUIRE и BOOST_CHECK
[arpan@tintin] ./a.out
Running 1 test case...
e2.cpp(11): error in "test1": check c == 2 failed
e2.cpp(12): fatal error in "test1": critical check yellow > red failed

*** 2 failures detected in test suite "enumtest"

Если для изучения проблемы необходимо выполнить проверку отдельных функций или методов класса, проще всего создать новый тест и вызвать процедуру, передав ей на вход аргументы и ожидаемые значения. Такой пример приведен в листинге 6.

Листинг 6. Использование Boost для проверки функций и методов класса
BOOST_AUTO_TEST(functionTest1) 
{
  BOOST_REQUIRE(myfunc1(99, ‘A’, 6.2) == 12);
  myClass o1(“hello world!\n”);
  BOOST_REQUIRE(o1.memoryNeeded( ) < 16);
}

Сопоставление образцов

Обычным делом является сравнение полученных результатов, возвращаемых какой-либо функцией, с эталонным значением. Для таких случаев также очень хорошо подходит макрос BOOST_CHECK. Кроме этого необходимо использовать класс output_test_stream из состава библиотеки Boost. Класс output_test_stream инициализируется с помощью эталонного файла (в следующем примере это файл run.log). Вывод функции C/C++ передается в объект output_test_stream, а затем вызывается процедура match_pattern. Это подробно рассмотрено в листинге 7.

Листинг 7. Сопоставление образцов с помощью эталонного файла
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <boost/test/output_test_stream.hpp>
using boost::test_tools::output_test_stream;

BOOST_AUTO_TEST_SUITE ( test ) 

BOOST_AUTO_TEST_CASE( test )
{
    output_test_stream output( "run.log", true );
    output << predefined_user_func( );
    BOOST_CHECK( output.match_pattern() );
}

BOOST_AUTO_TEST_SUITE_END( )

Сравнение величин с плавающей запятой

Одной из самых сложных проверок в регрессионном тестировании является сравнение величин с плавающей запятой. Взгляните на код листинга 8 – на первый взгляд кажется, что все работает нормально.

Листинг 8. Сравнение величин с плавающей запятой, которое не работает
#define BOOST_TEST_MODULE floatingTest
#include <boost/test/included/unit_test.hpp>
#include <cmath>

BOOST_AUTO_TEST_SUITE ( test ) 

BOOST_AUTO_TEST_CASE( test )
{
  float f1 = 567.0102;
  float result = sqrt(f1); // this could be my_sqrt; faster implementation
                                      // for some specific DSP like hardware
  BOOST_CHECK(f1 == result * result);  
}

BOOST_AUTO_TEST_SUITE_END( )

В процессе этого теста выполнение макроса BOOST_CHECK завершается с ошибкой, несмотря на использование функции sqrt, входящей в состав стандартной библиотеки. В чем же дело? Проблема сравнения величин с плавающей запятой заключается в точности: значения переменных f1 и result*result начинают различаться, начиная с некоторого знака после запятой. Для исправления этой ситуации в состав инструментов тестирования Boost входят макросы BOOST_WARN_CLOSE_FRACTION, BOOST_CHECK_CLOSE_FRACTION и BOOST_REQUIRE_CLOSE_FRACTION. Чтобы использовать любой из них, необходимо подключить предопределенный заголовок Boost floating_point_comparison.hpp. Все три этих макроса имеют одинаковый синтаксис, поэтому мы рассмотрим только вариант check (листинг 9).

Листинг 9. Синтаксис макроса BOOST_CHECK_CLOSE_FRACTION
BOOST_CHECK_CLOSE_FRACTION (left-value, right-value, tolerance-limit);

Вместо использования макроса BOOST_CHECK, как в листинге 9, попробуйте использовать макрос BOOST_CHECK_CLOSE_FRACTION с допустимой точностью 0,0001. Этот код приведен в листинге 10.

Листинг 10. Сравнение величин с плавающей запятой, которое работает
#define BOOST_TEST_MODULE floatingTest
#include <boost/test/included/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <cmath>

BOOST_AUTO_TEST_SUITE ( test ) 

BOOST_AUTO_TEST_CASE( test )
{
  float f1 = 567.01012;
  float result = sqrt(f1); // this could be my_sqrt; faster implementation
                                      // for some specific DSP like hardware
  BOOST_CHECK_CLOSE_FRACTION (f1, result * result, 0.0001);  
}

BOOST_AUTO_TEST_SUITE_END( )

Этот код работает прекрасно. Теперь изменим в листинге 10 допустимую точность на 0,0000001. Результат показан в листинге 11.

Листинг 11. Ошибка при сравнении из-за недопустимого предела точности
[arpan@tintin] ./a.out
Running 1 test case...
sq.cpp(18): error in "test": difference between f1{567.010132} and 
    result * result{567.010193} exceeds 1e-07

*** 1 failure detected in test suite "floatingTest"

Другая распространенная (и достаточно сложная) проблема, постоянно встречающаяся в рабочих версиях приложений, заключается в сравнении переменных типов double и float. Макрос BOOST_CHECK_CLOSE_FRACTION обладает замечательной особенностью, которая не позволяет выполнять такие сравнения. Левое и правое значения макроса должны быть одного типа: float или double. Если в листинге 12 переменная f1 будет иметь тип double, а результат result – тип float, во время компиляции произойдет ошибка.

Листинг 12. Ошибка: левый и правый аргументы BOOST_CHECK_CLOSE_FRACTION имеют разные типы
[arpan@tintin] g++ sq.cpp -I/u/c/lib/boost
/u/c/lib/boost/boost/test/test_tools.hpp: 
   In function 
       `bool boost::test_tools::tt_detail::check_frwd(Pred, 
       const boost::unit_test::lazy_ostream&, 
   boost::test_tools::const_string, size_t, 
   boost::test_tools::tt_detail::tool_level, 
   boost::test_tools::tt_detail::check_type, 
   const Arg0&, const char*, 
   const Arg1&, const char*, const Arg2&, const char*) 
   [with Pred = boost::test_tools::check_is_close_t, Arg0 = double, 
   Arg1 = float, Arg2 = boost::test_tools::fraction_tolerance_t<double>]':
sq.cpp:18:   instantiated from here
/u/c/lib/boost/boost/test/test_tools.hpp:523: error: no match for call to
 `(boost::test_tools::check_is_close_t) (const double&, const float&, 
     const boost::test_tools::fraction_tolerance_t<double>&)'

Поддержка пользовательских предикатов

Инструменты тестирования Boost могут работать с булевыми условиями. Можно расширить эти инструменты так, чтобы они поддерживали более сложные проверки, например определение идентичности содержимого двух списков или проверка выполнения определенного условия для всех элементов вектора. Также можно расширить макрос BOOST_CHECK, обеспечив поддержку пользовательских предикатов. Попробуем выполнить специальную проверку содержимого списка, сгенерированного определенной пользователем функцией языка C, и выясним, являются ли значения всех результирующих элементов больше 1. Функция пользовательской проверки должна возвращать тип boost::test_tools::predicate_result. Подробно это рассмотрено в листинге 13.

Листинг 13. Проверка сложных предикатов с помощью инструментов Boost
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>

boost::test_tools::predicate_result validate_list(std::list<int>& L1)
{ 
  std::list<int>::iterator it1 = L1.begin( );
  for (; it1 != L1.end( ); ++it1) 
   { 
     if (*it1 <= 1) return false; 
   }
  return true;
}

BOOST_AUTO_TEST_SUITE ( test ) 

BOOST_AUTO_TEST_CASE( test )
{
    std::list<int>& list1 = user_defined_func( );
    BOOST_CHECK( validate_list(list1) );
}

BOOST_AUTO_TEST_SUITE_END( )

Объект predicate_result имеет неявный конструктор, который принимает логическое значение. Это объясняет, почему код хорошо работает даже в тех случаях, когда ожидаемый и фактический возвращаемые типы validate_list различаются.

Еще одним способом проверки сложных предикатов с помощью Boost является использование макроса BOOST_CHECK_PREDICATE. Достоинством этого макроса является то, что он не использует predicate_result. Недостатком его является некоторая сложность синтаксиса. В макрос BOOST_CHECK_PREDICATE необходимо передавать имя функции и аргумент (или аргументы). Листинг 14 делает то же самое, что и листинг 13, просто в нем используются другие макросы. Обратите внимание на то, что возвращаемый тип validate_result теперь Boolean.

Листинг 14. Макрос BOOST_CHECK_PREDICATE
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>

bool validate_list(std::list<int>& L1)
{ 
  std::list<int>::iterator it1 = L1.begin( );
  for (; it1 != L1.end( ); ++it1) 
   { 
     if (*it1 <= 1) return false; 
   }
  return true;
}

BOOST_AUTO_TEST_SUITE ( test ) 

BOOST_AUTO_TEST_CASE( test )
{
    std::list<int>& list1 = user_defined_func( );
    BOOST_CHECK_PREDICATE( validate_list, list1 );
}

BOOST_AUTO_TEST_SUITE_END( )

Размещение нескольких тестовых пакетов в одном файле

Можно поместить несколько тестовых пакетов в один файл. Каждый тестовый пакет должен иметь пару макросов BOOST_AUTO_TEST_SUITE... BOOST_AUTO_TEST_SUITE_END, определенных внутри файла. В листинге 15 показаны два различных тестовых пакета, помещенные в один файл. При выполнении регрессий запустите исполняемый файл с помощью предопределенной опции –log_level=test_suite. Как видно из листинга 16, вывод, сгенерированный с использованием этой опции, оказывается более подробным и поддающимся быстрой отладке.

Листинг 15. Несколько тестовых пакетов, помещенных в один файл
#define BOOST_TEST_MODULE Regression
#include <boost/test/included/unit_test.hpp>

typedef struct {
                 int c;
                 char d;
                 double e;
                 bool f;
               } Node;

typedef union  {
                 int c;
                 char d;
                 double e;
                 bool f;
               } Node2;

BOOST_AUTO_TEST_SUITE(Structure)

BOOST_AUTO_TEST_CASE(Test1)
{
    Node n;
    BOOST_CHECK(sizeof(n) < 12);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(Union)

BOOST_AUTO_TEST_CASE(Test1)
{
    Node2 n;
    BOOST_CHECK(sizeof(n) == sizeof(double));
}

BOOST_AUTO_TEST_SUITE_END()

Ниже приведен вывод, полученный в результате выполнения кода листинга 15.

Листинг 16. Запуск нескольких тестовых пакетов с опцией –log_level
[arpan@tintin] ./a.out --log_level=test_suite
Running 2 test cases...
Entering test suite "Regression"
Entering test suite "Structure"
Entering test case "Test1"
m2.cpp(23): error in "Test1": check sizeof(n) < 12 failed
Leaving test case "Test1"
Leaving test suite "Structure"
Entering test suite "Union"
Entering test case "Test1"
Leaving test case "Test1"
Leaving test suite "Union"
Leaving test suite "Regression"

*** 1 failure detected in test suite "Regression"

Понимание структуры тестового пакета

До сих пор мы обсуждали тестовые пакеты Boost, не имеющие иерархической структуры. Теперь попытаемся создать с помощью Boost тестовый пакет, который тестирует программный продукт так, как обычно это видит пользователь внешних инструментов. Внутри самого тестового фреймворка обычно присутствуют несколько тестовых пакетов, каждый из которых проверяет определенные функции. Например, фреймворк для регрессионного тестирования процессора Word должен содержать тестовые пакеты для проверки поддержки шрифтов, различных файловых форматов и т. д. Каждый отдельный тестовый пакет содержит несколько модульных тестов. В листинге 17 приведен пример тестового фреймворка. Обратите внимание на то, что точкой входа, с которой начинается выполнение кода, должна являться процедура, названная (надлежащим образом) init_unit_test_suite.

Листинг 17. Создание главного тестового пакета для выполнения регрессий
#define BOOST_TEST_MODULE MasterTestSuite
#include <boost/test/included/unit_test.hpp>
using boost::unit_test;

test_suite*
init_unit_test_suite( int argc, char* argv[] )
{
    test_suite* ts1 = BOOST_TEST_SUITE( "test_suite1" );
    ts1->add( BOOST_TEST_CASE( &test_case1 ) );
    ts1->add( BOOST_TEST_CASE( &test_case2 ) );

    test_suite* ts2 = BOOST_TEST_SUITE( "test_suite2" );
    ts2->add( BOOST_TEST_CASE( &test_case3 ) );
    ts2->add( BOOST_TEST_CASE( &test_case4 ) );

    framework::master_test_suite().add( ts1 );
    framework::master_test_suite().add( ts2 );

    return 0;
}

Каждый тестовый пакет (например, пакет ts1 из листинга 17) создается с помощью макроса BOOST_TEST_SUITE. Макрос ожидает строку, являющуюся именем тестового пакета. Все тестовые пакеты, в конечном счете, оказываются добавленными в главный тестовый пакет с помощью метода add. Подобным образом вы создаете каждый тест с помощью макроса BOOST_TEST_CASE и добавляете его в тестовый пакет с помощью метода add. Также можно добавить модульные тесты в главный тестовый пакет, хотя делать этого не рекомендуется. Метод master_test_suite определен как часть пространства имен boost::unit_test::framework – внутри он реализует одноэлементное множество. Приведенный в листинге 18 код (содержится в исходниках самого Boost) объясняет, как это работает.

Листинг 18. Понимание метода master_test_suite
master_test_suite_t&
master_test_suite()
{
    if( !s_frk_impl().m_master_test_suite )
        s_frk_impl().m_master_test_suite = new master_test_suite_t;

    return *s_frk_impl().m_master_test_suite;
}

Тестовые модули, созданные с помощью макроса BOOST_TEST_CASE, принимают в качестве входных аргументов указатели на функции. Поэтому в листинге 17 функции test_case1, test_case2 и т. д. являются пустыми void-функциями, которые пользователь может запрограммировать так, как он захочет. Однако обратите внимание на то, что тестовая настройка Boost использует определенный небольшой объем памяти в куче; каждый вызов BOOST_TEST_SUITE сводится к новому вызову boost::unit_test::test_suite(<имя тестового пакета>).


Фикстуры

Тестовая фикстура – это заранее настроенное состояние среды, в которой выполняется тест. По завершении выполнения теста все настройки этой среды принимают свои первоначальные значения. Пример фикстуры показан в листинге 19.

Листинг 19. Простая фикстура Boost
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <iostream>

struct F {
    F() : i( 0 ) { std::cout << "setup" << std::endl; }
    ~F()          { std::cout << "teardown" << std::endl; }

    int i;
};

BOOST_AUTO_TEST_SUITE( test )

BOOST_FIXTURE_TEST_CASE( test_case1, F )
{
    BOOST_CHECK( i == 1 );
    ++i;
}

BOOST_AUTO_TEST_SUITE_END()

Результаты показаны в листинге 20.

Листинг 20. Результаты использования фикстуры Boost
[arpan@tintin] ./a.out
Running 1 test case...
setup
fix.cpp(16): error in "test_case1": check i == 1 failed
teardown

*** 1 failure detected in test suite "example"

Вместо использования макроса BOOST_AUTO_TEST_CASE в этом коде используется макрос BOOST_FIXTURE_TEST_CASE, в который передается дополнительный аргумент. Методы конструктора и деструктора этого объекта выполняют необходимую настройку и очистку. Это подтверждает беглый взгляд на заголовок модуля Boost unit_test_suite.hpp (листинг 21).

Листинг 21. Определение фикстуры Boost в заголовке unit_test_suite.hpp
#define BOOST_FIXTURE_TEST_CASE( test_name, F )       \
struct test_name : public F { void test_method(); };                   \
                                                                                        \
static void BOOST_AUTO_TC_INVOKER( test_name )()       \
{                                                                                       \
    test_name t;                                                                        \
    t.test_method();                                                                    \
}                                                                                       \
                                                                                        \
struct BOOST_AUTO_TC_UNIQUE_ID( test_name ) {};       \
                                                                                        \
BOOST_AUTO_TU_REGISTRAR( test_name )(                     \
    boost::unit_test::make_test_case(                                            \
        &BOOST_AUTO_TC_INVOKER( test_name ), #test_name ),                  \
    boost::unit_test::ut_detail::auto_tc_exp_fail<                                   \
        BOOST_AUTO_TC_UNIQUE_ID( test_name )>::instance()->value() );   \
                                                                                        \
void test_name::test_method()                                                           \

Boost публично наследует класс из структуры struct F (см. листинг 19) и создает на его основе объект. В соответствии с правилами публичного наследования C++ все защищенные и публичные переменные класса struct напрямую доступны из последующей функции. Обратите внимание на то, что изменяемая в листинге 19 переменная i принадлежит внутреннему объекту t с типом F (см. листинг 20). Вполне нормально, если в тестовом пакете регрессионного тестирования явная инициализация (следовательно, необходимость использования фикстур) требуется лишь для двух-трех тестов. В листинге 22 приведен тестовый пакет, в котором только один из трех тестов использует фикстуру.

Листинг 22. Тестовый пакет Boost, содержащий тесты с фикстурами и без фикстур
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <iostream>

struct F {
    F() : i( 0 ) { std::cout << "setup" << std::endl; }
    ~F()          { std::cout << "teardown" << std::endl; }

    int i;
};

BOOST_AUTO_TEST_SUITE( test )

BOOST_FIXTURE_TEST_CASE( test_case1, F )
{
    BOOST_CHECK( i == 1 );
    ++i;
}

BOOST_AUTO_TEST_CASE( test_case2 )
{
    BOOST_REQUIRE( 2 > 1 );
}

BOOST_AUTO_TEST_CASE( test_case3 )
{
    int i = 1;
    BOOST_CHECK_EQUAL( i, 1 );
    ++i;
}

BOOST_AUTO_TEST_SUITE_END()

В листинге 22 содержатся фикстуры, определенные и используемые только в одном тесте. С помощью макроса BOOST_GLOBAL_FIXTURE (<имя фикстуры>) Boost также позволяет пользователям определять и использовать глобальные фикстуры. Можно определить любое количество глобальных фикстур, позволяющих разделять код инициализации. Пример использования глобальной фикстуры приведен в листинге 23.

Листинг 23. Использование глобальных фикстур для инициализации регрессии
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <iostream>

struct F {
    F()           { std::cout << "setup" << std::endl; }
    ~F()         { std::cout << "teardown" << std::endl; }
};

BOOST_AUTO_TEST_SUITE( test )
BOOST_GLOBAL_FIXTURE( F );
BOOST_AUTO_TEST_CASE( test_case1 )
{
    BOOST_CHECK( true );
}
BOOST_AUTO_TEST_SUITE_END()

В случае нескольких фикстур их настройка и уничтожение происходят в том порядке, в котором они были объявлены. В листинге 24 конструктор и деструктор класса F вызываются прежде конструктора и деструктора класса F2 соответственно.

Листинг 24. Использование нескольких глобальных фикстур в регрессии
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <iostream>

struct F {
    F()           { std::cout << "setup" << std::endl; }
    ~F()         { std::cout << "teardown" << std::endl; }
};

struct F2 {
    F2()           { std::cout << "setup 2" << std::endl; }
    ~F2()         { std::cout << "teardown 2" << std::endl; }
};

BOOST_AUTO_TEST_SUITE( test )
BOOST_GLOBAL_FIXTURE( F );
BOOST_GLOBAL_FIXTURE( F2 );

BOOST_AUTO_TEST_CASE( test_case1 )
{
    BOOST_CHECK( true );
}
BOOST_AUTO_TEST_SUITE_END()

Обратите внимание на то, что нельзя использовать глобальные фикстуры в качестве объектов отдельных тестов. Невозможно также получить прямой доступ к их публичным/защищенным нестатическим методам или переменным внутри теста.


Заключение

Эта статья познакомила вас с Boost – одним из наиболее мощных фреймворков регрессионного тестирования с открытым исходным кодом. Вы узнали об основных проверках Boost, сопоставлении образцов, сравнении величин с плавающей запятой, пользовательских проверках, организации тестовых пакетов (как ручной, так и автоматической) и фикстурах. Для получения дальнейшей информации обратитесь к документации Boost. Остальные статьи этой серии познакомят вас с другими фреймворками open source для регрессионного тестирования, такими как cppUnit.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=AIX и UNIX
ArticleID=785241
ArticleTitle=Инструменты Open Source для модульного тестирования C/C++: Часть 1. Знакомство с фреймворком Boost
publish-date=01132012