CTinyTester

Что это такое

Однажды захотелось мне попробовать использовать один из основных принципов экстремального программирования - "Пиши тесты раньше программы", скачал, значит, CppUnit (аналог JUnit). Попробовал все скомпилировать под Visual C++ - вроде бы все прошло без проблем. Ну, думаю, ладно, теперь должно все быть хорошо и под Visual C++ 7.0 (все-таки в 6-ке ошибок побольше будет). Однако получился облом - куча ошибок при линковке самих библиотек да и скомпилированные у меня что-то не захотели работать. Правда, если честно, я и не особо настаивал, т.к. уже давно хотелось написать свой UnitTest - просто из интереса, не особо навороченный. Ну а тут уж такой повод появился :) Так и появился этот небольшой проект под названием CTinyTester.

Основные требования к тестеру были такие:

  1. Должен работать под Visual C++ 7.0 и соответственно выше. Но за неимением пока 7.1 (подожду уж выхода Whidbey) главное, чтобы работало под 7-кой
  2. Гибкий вывод результатов тестов. Т.е. чтобы можно было бы без проблем менять внешний вид результатов
  3. Возможность ловить нужные исключения
  4. 3 вида прохождения тестов:
    • Успешний
    • Ошибочный
    • Ошибочный из-за неожиданного исключения
  5. Основные тесты вроде логических утверждений и ловли заданных исключений

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

Основные классы

Все классы объединены в пространство имен tut (от слов Tiny Unit Test). В этом проекте содержатся два основных класса:

  • tut::CTinyTester
  • tut::CTestLog

tut::CTinyTester - это основа всех тестов. Именно в нем содержатся все методу, которые можно назвать тестами. Подробности чуть попозже.
tut::CTestLog - это базовый класс для классов, которые выводят результаты тестов.

Сами классы и пример использования вы можете скачать здесь.

CTestLog

Итак, в первую очередь вы должны создать класс, производный от CTestLog. CTestLog - это базовый класс для классов, которые представляют результаты тестирования в какой-либо форме (далее я их буду называль логеры или просто логи). Сам CTestLog абстрактный, поэтому надо будет делать классы производные от него. Например, в архиве лежит класс CTextLog, он записывает результаты тестов в текстовый файл. С ним познакомимся попозже. Основные методы класса CTestLog описаны в документации. В двух словах о их можно сказать так:

  1. Перед началом всех тестов вызывается метод Begin().
  2. После окончания всех тестов вызывается метод End().
  3. Перед каждым тестом вызывается метод BeginTest(), в который передается номер теста по счету и его название.
  4. Метод TestSuccess() вызывается, если очередной тест прошел успешно.
  5. Если тест не был пройден, то вызывается метод TestFailed().
  6. Если тест не был пройден удачно из-за возникшего исключения, то вызывается метод TestExceptionFailed().
  7. Для лучшего восприятия результатов, тесты можно делить на группы тестов (это все условности, которые на сам процесс тестирования не влияют). Для этого вызывают метод NextGroup().

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

Как пример логера я включил в архив CTextLog - логер, который пишет результаты в обычный текстовый файл наподобие . Его описание также есть в докуменнтации. Скажу только, что для его создания надо передать в конструктор имя файла, в который будет писать логер.

CTinyTester

А это основа всех тестов. Именно в его методах и проходят тесты. Вам нужно создать класс, производный от CTinyTester и переопределить в нем чисто виртуальную функцию void TestBody(); Она должна быть защищенной (protected). Именно в ней и происходят тесты. Методы для тестирования вы можете посмотреть в документации (их немного), найдя в разделе "Группы" подраздел "Собственно сами тесты". А макросы для тестирования исключений, описаны в подразделе "Макросы, предназначенные для отладки исключений". То есть в переопределенном методе TestBody() вы используете эти методы и макросы для составления тестов. К самим классам в архиве прилагается пример тестированяи класса для работы с реестром, там вы можете посмотреть все это на примере.

Итого

Типичная функция main() для тестирования может выглядеть следующим образом (взято из примера):

 #include "TinyTest\TextLog.h"
 #include "RegTester.h"

 int APIENTRY WinMain(HINSTANCE hInstance,
                      HINSTANCE hPrevInstance,
                      LPSTR     lpCmdLine,
                      int       nCmdShow)
 {
        try
        {
                tut::CTextLog Log("reg.log");
                CRegTester Tester (&Log);
                Tester.Start();
        }
        catch (tut::CTextLog::EFileOpenError)
        {
        }

        return 0;
 }

Здесь CRegTester - это класс, производный от tut::CTinyTester. В данном случае его описание выглядит так:

 #include "tinytest\tinytester.h"

 #include "Registry\RegistryReader.h"
 #include "Registry\SimplyRegistry.h"
 #include "Registry\RegistryWriter.h"

 using reg::CRegistryReader;
 using reg::CRegistryWriter;
 using reg::CSimplyRegistry;

 class CRegTester : public tut::CTinyTester
 {
 private:
        // Подготовимся к тесту
        bool Prepare();

        // Тестирование класса для записи
        void TestWriter();

        // Тестирование класса для чтения
        void TestReader();

        // Тестирование класса для быстрого доступа
        void TestSimply();
 public:
        CRegTester(tut::CTestLog *pLog);
        virtual ~CRegTester(void);

 protected:
        void TestBody();
 };

Не обращайте внимание на приватные члены - они сделаны просто для разделения тестов. Метод TestBody() в нашем случае выглядит так:

 void CRegTester::TestBody()
 {
        if (!Prepare())
        {
                return;
        }

        TestReader();
        TestWriter();
        TestSimply();
 }

Здесь Prepare() просто подготавливает записи реестра, на которых будут проходить испытания. У вас его может и не быть. Таким образом, простейший класс для тестирования может выглядить так:

 #include "tinytest\tinytester.h"

 #include "Registry\RegistryReader.h"
 #include "Registry\SimplyRegistry.h"
 #include "Registry\RegistryWriter.h"

 using reg::CRegistryReader;
 using reg::CRegistryWriter;
 using reg::CSimplyRegistry;

 class CMyTester : public tut::CTinyTester
 {
 public:
        CRegTester(tut::CTestLog *pLog);
        virtual ~CRegTester(void);

 protected:
        void TestBody();
 };

Но не забудьте вызвать конструктор базового класса, в который также передается указатель на класс логера:

 CMyTester::CMyTester(tut::CTestLog *pLog): CTinyTester (pLog)
 {
 }

Заключение

Ну вот пожалуй и все. За более подробным описанием обращайтесь к документации, как говорится RTFM :)



Подписаться на комментарии
Автор:
Тема:
 Ваш комментарий
 
 
Введите код 508
 

simon 14.11.2007 - 11:09

Очень интересный юнит тестер