Введение в среду Google C++ Testing Framework

Узнайте об основных возможностях, обеспечивающих удобное использование в рабочей среде

Компания Google представила интересную и легкую в использовании среду с открытым кодом, предназначенную для разработки модульных тестов для языка C/C++. Эта статья, основанная на версии 1.4, познакомит читателей с основными полезными возможностями системы Google C++ Testing Framework.

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

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



27.12.2010

Для чего нужна Google C++ Testing Framework?

Существует множество причин использовать эту платформу. В этом разделе будут рассмотрены некоторые из них.

Некоторые категории тестов сталкиваются с проблемами памяти, проявляющимися только при определенных условиях. Среда тестирования от Google имеет отличную поддержку для обработки таких ситуаций. С помощью платформы от Google вы можете выполнять один и тот же тест тысячи раз. При первом обнаружении проблемы автоматически вызывается отладчик. Все это может быть проделано с помощью всего лишь двух опций командной строки: --gtest_repeat=1000 --gtest_break_on_failure.

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

Запустить тесты достаточно просто. Просто вызовите встроенный макрос RUN_ALL_TESTS вместо того, чтобы отдельно создавать для выполнения теста новый или производный выполняемый класс. Это существенное отличие от таких платформ, как, например, CppUnit.

Вы можете легко создать отчет в формате XML (Extensible Markup Language) с помощью опции --gtest_output="xml:<имя файла>". Для вывода XML-отчета в таких системах, как CppUnit и CppTest, вам необходимо написать существенно больший объем кода.


Создание базового теста

Рассмотрим макет простой функции вычисления квадратного корня, показанный в листинге 1.

Листинг 1. Макет функции вычисления квадратного корня
double square-root (const double);

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

Листинг 2. Модульный тест для функции вычисления квадратного корня
#include "gtest/gtest.h"

TEST (SquareRootTest, PositiveNos) { 
    EXPECT_EQ (18.0, square-root (324.0));
    EXPECT_EQ (25.4, square-root (645.16));
    EXPECT_EQ (50.3321, square-root (2533.310224));
}

TEST (SquareRootTest, ZeroAndNegativeNos) { 
    ASSERT_EQ (0.0, square-root (0.0));
    ASSERT_EQ (-1, square-root (-22.0));
}

В листинге 2 создается иерархия тестов с именем SquareRootTest, а затем к ней добавляются два модульных теста с именами PositiveNos и ZeroAndNegativeNos. TEST – это встроенный макрос, определенный в модуле gtest.h (доступен с загружаемым исходным кодом) и помогающий создать иерархию. EXPECT_EQ и ASSERT_EQ также являются макросами — в первом случае контрольного примера выполнение теста продолжается даже при возникновении ошибки, в то время как во втором случае в этой ситуации выполнение теста прекращается. Безусловно, если квадратный корень из 0 отнюдь не 0, то в любом случае, тестировать особо нечего. Вот почему в тесте ZeroAndNegativeNos используется макрос ASSERT_EQ, тогда как в тесте PositiveNos используется макрос EXPECT_EQ, показывающий, сколько было случаев, когда функция вычисления квадратного корня завершалась с ошибкой, но выполнение теста не прекращалось.


Запуск первого теста

Теперь, когда вы создали ваш первый базовый тест, пришло время запустить его. В листинге 3 приведен код основной программы, выполняющей тестирование.

Листинг 3. Запуск теста функции вычисления квадратного корня
#include "gtest/gtest.h"

TEST(SquareRootTest, PositiveNos) { 
    EXPECT_EQ (18.0, square-root (324.0));
    EXPECT_EQ (25.4, square-root (645.16));
    EXPECT_EQ (50.3321, square-root (2533.310224));
}

TEST (SquareRootTest, ZeroAndNegativeNos) { 
    ASSERT_EQ (0.0, square-root (0.0));
    ASSERT_EQ (-1, square-root (-22.0));
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

Метод ::testing::InitGoogleTest работает в соответствии со своим названием – он инициализирует платформу, и его необходимо вызвать перед функцией RUN_ALL_TESTS. Функцию RUN_ALL_TESTS необходимо вызвать всего один раз, так как множественные вызовы этой функции не поддерживаются, поскольку конфликтуют с некоторыми расширенными возможностями платформы. Обратите внимание на то, что функция RUN_ALL_TESTS автоматически определяет и запускает все тесты, определенные с помощью макроса TEST. По умолчанию результат выводится на устройство стандартного вывода. В листинге 4 показан результат выполнения теста.

Листинг 4. Вывод теста функции вычисления квадратного корня
Running main() from user_main.cpp
[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from SquareRootTest
[ RUN      ] SquareRootTest.PositiveNos
..\user_sqrt.cpp(6862): error: Value of: sqrt (2533.310224)
  Actual: 50.332
Expected: 50.3321
[  FAILED  ] SquareRootTest.PositiveNos (9 ms)
[ RUN      ] SquareRootTest.ZeroAndNegativeNos
[       OK ] SquareRootTest.ZeroAndNegativeNos (0 ms)
[----------] 2 tests from SquareRootTest (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (10 ms total)
[  PASSED  ] 1 test.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] SquareRootTest.PositiveNos

 1 FAILED TEST

Дополнительные опции Google C++ Testing Framework

Из листинга 3 видно, что функция InitGoogleTest принимает и загружает аргументы в тестовую инфраструктуру. В этом разделе рассматриваются некоторые полезные действия, которые вы можете проделывать с этими аргументами.

Вы можете выгрузить вывод в файл формата XML, указав в командной строке опцию --gtest_output="xml:report.xml". Конечно, вы можете заменить имя report.xml любым другим на свое усмотрение.

Существуют определенные тесты, которые иногда завершаются с ошибкой, но в большинстве случаев выполняются успешно. Это присуще проблемам, связанным с ошибкой в памяти. В таких случаях возможность выявить ошибку будет выше, если тесты выполняются множество раз. Если вы укажете в командной строке опции --gtest_repeat=2 --gtest_break_on_failure, то один и тот же тест будет выполнен дважды. Если тест завершается с ошибкой, автоматически вызывается отладчик.

Не все тесты бывает необходимо запускать каждый раз, особенно если вы вносите изменения в код только определенных модулей. Для этого система тестирования от Google предоставляет в ваше распоряжение опцию --gtest_filter=<маска>. Маска представляет собой набор регулярных выражений, разделенных двоеточиями (:). Например, опция --gtest_filter=* запускает все тесты, а опция --gtest_filter=SquareRoot* запускает только тесты группы SquareRootTest. Если вы хотите запустить тесты группы SquareRootTest только для положительных значений, используйте опцию --gtest_filter=SquareRootTest.*-SquareRootTest.Zero*. Обратите внимание на то, что маска SquareRootTest.* включает выполнение всех тестов группы SquareRootTest, а маска -SquareRootTest.Zero* исключает выполнение тестов, имена которых начинаются с Zero.

В листинге 5 приведен пример запуска группы тестов SquareRootTest с использованием опций gtest_output, gtest_repeat и gtest_filter.

Листинг 5. Запуск тестов SquareRootTest с опциями gtest_output, gtest_repeat и gtest_filter
[arpan@tintin] ./test_executable --gtest_output="xml:report.xml" --gtest_repeat=2 --
gtest_filter=SquareRootTest.*-SquareRootTest.Zero*

Repeating all tests (iteration 1) . . .

Note: Google Test filter = SquareRootTest.*-SquareRootTest.Z*
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from SquareRootTest
[ RUN      ] SquareRootTest.PositiveNos
..\user_sqrt.cpp (6854): error: Value of: sqrt (2533.310224)
  Actual: 50.332
Expected: 50.3321
[  FAILED  ] SquareRootTest.PositiveNos (2 ms)
[----------] 1 test from SquareRootTest (2 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (20 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] SquareRootTest.PositiveNos
 1 FAILED TEST

Repeating all tests (iteration 2) . . .

Note: Google Test filter = SquareRootTest.*-SquareRootTest.Z*
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from SquareRootTest
[ RUN      ] SquareRootTest.PositiveNos
..\user_sqrt.cpp (6854): error: Value of: sqrt (2533.310224)
  Actual: 50.332
Expected: 50.3321
[  FAILED  ] SquareRootTest.PositiveNos (2 ms)
[----------] 1 test from SquareRootTest (2 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (20 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] SquareRootTest.PositiveNos
 1 FAILED TEST

Временное отключение тестов

Предположим, вы приостановили выполнение определенной части кода. Можно ли временно отключить тест? Да, просто добавьте префикс DISABLE_ к логическому имени теста или к имени отдельного его модуля, и он перестанет выполняться. В листинге 6 показано, что вам необходимо сделать для того, чтобы отключить выполнение теста PositiveNos, приведенного ранее в листинге 2.

Листинг 6. Временное отключение тестов
#include "gtest/gtest.h"

TEST (DISABLE_SquareRootTest, PositiveNos) { 
    EXPECT_EQ (18.0, square-root (324.0));
    EXPECT_EQ (25.4, square-root (645.16));
    EXPECT_EQ (50.3321, square-root (2533.310224));
}

OR

TEST (SquareRootTest, DISABLE_PositiveNos) { 
    EXPECT_EQ (18.0, square-root (324.0));
    EXPECT_EQ (25.4, square-root (645.16));
    EXPECT_EQ (50.3321, square-root (2533.310224));
}

Обратите внимание на то, что по окончании выполнения тестов среда тестирования Google выводит предупреждение о том, что имеются отдельные отключенные тесты. Это показано в листинге 7.

Листинг 7. Предупреждение среды Google об отключенных тестах
1 FAILED TEST
  YOU HAVE 1 DISABLED TEST

Если вы хотите продолжить выполнение отключенных тестов, укажите в командной строке опцию -gtest_also_run_disabled_tests. В листинге 8 показан результат работы программы, когда запускается тест DISABLE_PositiveNos.

Листинг 8. Google позволяет запускать отключенные тесты
[----------] 1 test from DISABLED_SquareRootTest
[ RUN      ] DISABLED_SquareRootTest.PositiveNos
..\user_sqrt.cpp(6854): error: Value of: square-root (2533.310224)
  Actual: 50.332
Expected: 50.3321
[  FAILED  ] DISABLED_SquareRootTest.PositiveNos (2 ms)
[----------] 1 test from DISABLED_SquareRootTest (2 ms total)

[  FAILED  ] 1 tests, listed below:
[  FAILED  ] SquareRootTest. PositiveNos

Все о правилах

Платформа тестирования компании Google содержит в себе множество предопределенных правил. Существует два типа правил – правила, имена которых начинаются с ASSERT_, и правила, имена которых начинаются с EXPECT_. Правила ASSERT_* прерывают выполнение программы при нарушении условия, тогда как в случае правил EXPECT_* выполнение программы продолжается. В обоих случаях при нарушении условий система выводит имя файла, номер строки и сообщение, которое вы можете определить самостоятельно. Одними из несложных правил являются ASSERT_TRUE (условие) и ASSERT_NE (значение1, значение2). Первое из них предполагает, что указанное условие всегда должно выполняться, а второе – что два значения не должны совпадать. Эти правила также работают с определенными пользователями типами данных, но при этом вы должны выполнить перегрузку соответствующего оператора сравнения (==, !=, <= и т. д.).


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

Google предоставляет в ваше распоряжение макросы для сравнения чисел с плавающей запятой, представленные в листинге 9.

Листинг 9. Макросы для сравнения чисел с плавающей запятой
ASSERT_FLOAT_EQ (expected, actual)
ASSERT_DOUBLE_EQ (expected, actual) 
ASSERT_NEAR (expected, actual, absolute_range)

EXPECT_FLOAT_EQ (expected, actual)
EXPECT_DOUBLE_EQ (expected, actual) 
EXPECT_NEAR (expected, actual, absolute_range)

Почему нужны отдельные макросы для сравнения чисел с плавающей запятой? Разве не будет работать правило ASSERT_EQ? Ответ заключается в том, что ASSERT_EQ и связанные с ним макросы могут работать, а могут и не работать, и поэтому лучше использовать макросы, специально предназначенные для сравнения чисел с плавающей запятой. Обычно различные центральные процессоры и рабочие окружения хранят числа с плавающей запятой по-разному, и простые сравнения ожидаемых и фактических значений не работают. Например, правило ASSERT_FLOAT_EQ (2.00001, 2.000011) сработает – Google не возвращает ошибок, если значения совпадают с точностью до четырех десятичных знаков. Если вам необходима более высокая точность, используйте правило ASSERT_NEAR (2.00001, 2.000011, 0.0000001), и тогда вы получите ошибку, показанную в листинге 10.

Листинг 10. Сообщение об ошибке, выводимое правилом ASSERT_NEAR
Math.cc(68): error: The difference between 2.00001 and 2.000011 is 1e-006, which exceeds
0.0000001, where
2.00001 evaluates to 2.00001,
2.000011 evaluates to 2.00001, and
0.0000001 evaluates to 1e-007.

"Смертельные" тесты

В системе Google C++ Testing Framework имеется интересная категория правил (ASSERT_DEATH, ASSERT_EXIT и т. д.), называющихся "смертельными" правилами. Эти правила используются для проверки того, было ли какое-либо сообщение об ошибке получено в результате передачи на вход функции некорректных данных, или же работа этой функции была завершена в соответствии с заранее определенным кодом завершения. Например, возвращаясь к листингу 3, неплохо было бы получить сообщение об ошибке при выполнении функции square-root (-22.0) и выйти из программы со статусом -1 вместо получения значения -1.0. В листинге 11 для такого сценария используется правило ASSERT_EXIT.

Листинг 11. Выполнение "смертельного" теста платформы Google
#include "gtest/gtest.h"

double square-root (double num) { 
    if (num < 0.0) { 
        std::cerr << "Error: Negative Input\n";
        exit(-1);
    }
    // Код для 0 и положительных чисел…
}

TEST (SquareRootTest, ZeroAndNegativeNos) { 
    ASSERT_EQ (0.0, square-root (0.0));
    ASSERT_EXIT (square-root (-22.0), ::testing::ExitedWithCode(-1), "Error: 
Negative Input");
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

Правило ASSERT_EXIT проверяет, была ли функция завершена в соответствии с заданным кодом завершения (т. е. аргументом функций exit или _exit), и сравнивает заключенную в кавычки строку с тем, что выводит функция на стандартное устройство сообщений об ошибках. Обратите внимание на то, что сообщения об ошибках должны выводиться на устройство std::cerr, а не на std::cout. В листинге 12 представлены макеты для правил ASSERT_DEATH и ASSERT_EXIT.

Листинг 12. Макеты для "смертельных" правил
ASSERT_DEATH(оператор, ожидаемое_сообщение)
ASSERT_EXIT(оператор, предикат, ожидаемое_сообщение)

Google предоставляет в ваше распоряжение встроенный предикат ::testing::ExitedWithCode(код_завершения). Результат этого предиката будет истинным только в том случае, если программа завершается с тем же кодом завершения, что и аргумент код_завершения этого предиката. Правило ASSERT_DEATH проще, чем правило ASSERT_EXIT – оно просто сравнивает сообщение об ошибке, выводимое на стандартное устройство, с ожидаемым сообщением, заданным пользователем.


Понимание тестовых фикстур

Перед началом модульного тестирования, как правило, выполняется некоторая пользовательская инициализация. Например, если вы пытаетесь определить время выполнения теста и занимаемый им объем памяти, то вам необходимо написать для этого теста определенный код, позволяющий измерить эти значения. Здесь в дело вступают фикстуры, помогающие вам настраивать такие тестовые окружения. В листинге 13 показано, как выглядит класс тестовой фикстуры.

Листинг 13. Класс тестовой фикстуры
class myTestFixture1: public ::testing::test { 
public: 
   myTestFixture1( ) { 
       // код инициализации
   } 

   void SetUp( ) { 
       // код, который будет выполнен перед началом теста 
   }

   void TearDown( ) { 
       // код, который будет выполнен сразу по завершении теста
       // при необходимости здесь можно вызывать исключения
   }

   ~myTestFixture1( )  { 
       // очистка всех ресурсов, вызов исключений не допускается
   }

   // сюда можно поместить необходимые вам пользовательские объекты данных 
};

Класс фикстуры является производным от класса ::testing::test, объявленного в модуле gtest.h. В листинге 14 приведен пример, в котором используется класс фикстуры. Обратите внимание на то, что в данном примере вместо макроса TEST используется макрос TEST_F.

Листинг 14. Пример использования фикстуры
TEST_F (myTestFixture1, UnitTest1) { 
   
.
}

TEST_F (myTestFixture1, UnitTest2) { 
   
.
}

Ниже перечислены несколько условий, которые необходимо учитывать при использовании фикстур:

  • Вы можете выполнить инициализацию или распределение ресурсов как с помощью конструктора, так и с помощью метода SetUp – выбор остается за вами.
  • Вы можете освобождать ресурсы с помощью метода TearDown или с помощью функции деструктора. Однако если вы хотите использовать обработку исключений, вы должны делать это только через метод TearDown, поскольку вызов исключения в деструкторе может привести к непредсказуемым результатам.
  • Макросы правил Google могут вызывать исключения на тех платформах, для которых они доступны в будущих версиях. Поэтому для лучшего сопровождения программ хорошо использовать макросы правил в коде метода TearDown.
  • Одни и те же фикстуры не используются в различных тестах. Для каждого нового модульного теста система создает новую тестовую фикстуру. Так что в листинге 14 метод SetUp (пожалуйста, используйте здесь правильную орфографию) вызывается дважды, потому что создаются два объекта myFixture1.

Заключение

В этой статье мы лишь поверхностно рассмотрели платформу Google C++ Testing Framework. Подробная документация по этой платформе доступна на сайте компании Google. Для профессиональных разработчиков я рекомендую ознакомиться с дополнительными материалами об открытых платформах регрессионного тестирования, таких как Boost unit test framework и 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, Open source
ArticleID=605180
ArticleTitle=Введение в среду Google C++ Testing Framework
publish-date=12272010