tdd - это когда чуваки говорят: «зачем нам думать, лучше напишем ка мы ещё этих юнит тестов»
@hirthwork
Раньше я никогда не писал unit тестов. Ну, во-первых, я их всегда считал ненужной тратой времени. Во-вторых: я себе слабо представлял в чем их необходимость. В-третьих: я никак не мог понять - как можно тратить время на сраные тесты, когда можно писать код.
И вот буквально пару дней назад я все таки созрел для написания своих первых тестов. Тестировал я функцию, которая вытягивае метаданные из fb2 и epub книжек (Да, я решил написать убийцу calibre). Так как проект написан на qt, то и в качестве средства написания тестов я выбрал QtTest. Ща я покажу немного кода, который необходим был для моих тестов: Вы его всегда сможете адаптировать для себя.
Я сразу приведу весь код, а заодно протестирую еще и работу LJ с github Gist. А она - полное говно. Весь код ломает как хрен знает что. Буду продолжать юзать нормальные хайлатеры.Итак, ключевые моменты написания простого unit теста в Qt использую QtTest:
CMakeLists.txt:
// создаем опцию для включения/выключения тестов
option (TESTS_SHPICK "Enable Shpick tests" OFF)
// инклудим QtTest
if (TESTS_SHPICK)
set (QT_USE_QTTEST TRUE)
endif ()
include (${QT_USE_FILE})
if (TESTS_SHPICK)
// используем файл конфугурации для передачи в код значени ${CMAKE_CURRENT_SOURCE_DIR} - там у нас лежат тестовые книги
configure_file (tests/test.h.in tests/test.h)
// инклудим директорию с тестами
include_directories (${CMAKE_CURRENT_BINARY_DIR}/tests)
QT4_WRAP_CPP (PARSERMANAGERTEST_MOC "tests/parsermanagertest.cpp")
// далее обычные действия по созданию бинарника и линковке его с необходимыми либами
add_executable (bm_shpick_parsermanagertest WIN32
tests/parsermanagertest.cpp
parsermanager.cpp
bookparsers/bookfactory.cpp
bookparsers/epub.cpp
bookparsers/fb2.cpp
${PARSERMANAGERTEST_MOC}
)
target_link_libraries (bm_shpick_parsermanagertest
${QT_LIBRARIES}
${BLUMBERG_LIBRARIES}
${QUAZIP_LIBRARIES}
)
// добавлям тест
add_test (ParserManager bm_shpick_parsermanagertest)
endif ()
parsermanagertest.h
using namespace Blumberg::Shpick;
class TestParserManager : public QObject
{
Q_OBJECT
private slots:
// Метод я называю так же как и реальный метод в проекте
void ParseBook ()
{
// Вот тут как раз используется переменная PLUGIN_SOURCE_DIR, равная ${CMAKE_CURRENT_SOURCE_DIR}
const QString& fb2Path = QString (PLUGIN_SOURCE_DIR) + "/tests/test.fb2";
auto fb2Parser = MakeBookParser (fb2Path);
if (!fb2Parser)
// QFAIL используется там, где вам нужно показать фейл теста.
// Например у меня это происходит, если нету парсера для книги
QFAIL ("Book parsers was not created fro FB2 format");
Blumberg::Book fb2Book = fb2Parser (fb2Path);
if (!fb2Book.isValid ())
QFAIL ("Test book is not valid: fb2 format");
// Дальше идут сами тестыю Проверяем значения, которые возвращает мой парсер со значениями из файлов
QCOMPARE (fb2Book.TitleInfo_.Ganres_.count (), 3);
QCOMPARE (fb2Book.TitleInfo_.Ganres_.at (0), QString ("architecture"));
QCOMPARE (fb2Book.TitleInfo_.Ganres_.at (1), QString ("art"));
QCOMPARE (fb2Book.TitleInfo_.Ganres_.at (2), QString ("art_instr"));
QCOMPARE (fb2Book.TitleInfo_.Authors_.count (), 3);
QCOMPARE (fb2Book.TitleInfo_.Authors_.at (0).LastName_, QString ("last-name1"));
QCOMPARE (fb2Book.TitleInfo_.Authors_.at (0).HomePages_.count (), 3);
QCOMPARE (fb2Book.TitleInfo_.Authors_.at (0).HomePages_.at (2), QUrl ("home-page3"));
QCOMPARE (fb2Book.TitleInfo_.Authors_.at (0).EMails_.count (), 3);
QCOMPARE (fb2Book.TitleInfo_.Authors_.at (0).EMails_.at (1), QString ("email2"));
QCOMPARE (fb2Book.TitleInfo_.Authors_.at (1).MiddleName_, QString ("middle-name2"));
QCOMPARE (fb2Book.TitleInfo_.Authors_.at (2).FirstName_, QString ("first-name3"));
QCOMPARE (fb2Book.TitleInfo_.Authors_.at (2).NickName_, QString ("nickname3"));
QCOMPARE (fb2Book.TitleInfo_.Title_, QString ("book-title1"));
QCOMPARE (fb2Book.TitleInfo_.Tags_, QStringList ("keywords1"));
QCOMPARE (fb2Book.TitleInfo_.WrittenDate_, QString ("1900-01-01"));
QCOMPARE (fb2Book.TitleInfo_.OriginalLanguage_, QString ("en"));
QCOMPARE (fb2Book.TitleInfo_.TextLanguage_, QString ("en"));
QCOMPARE (fb2Book.TitleInfo_.Translators_.count (), 1);
QCOMPARE (fb2Book.TitleInfo_.Translators_.at (0).FirstName_, QString ("first-name4"));
QCOMPARE (fb2Book.TitleInfo_.Seria_, QString ("name1"));
QCOMPARE (fb2Book.TitleInfo_.Episode_, 1);
QCOMPARE (fb2Book.PublishInfo_.BookName_, QString ("book-name1"));
QCOMPARE (fb2Book.PublishInfo_.Publisher_, QString ("publisher1"));
QCOMPARE (fb2Book.PublishInfo_.City_, QString ("city1"));
QCOMPARE (fb2Book.PublishInfo_.PublishedYear_, 1900);
QCOMPARE (fb2Book.PublishInfo_.ISBN_, QString ("isbn1"));
// Тут все тоже самое, только для epub формата.
const QString& epubPath = QString (PLUGIN_SOURCE_DIR) + "/tests/test.epub";
auto epubParser = MakeBookParser (epubPath);
if (!epubParser)
QFAIL ("Book parsers was not created for EPUB format");
Blumberg::Book epubBook = epubParser (epubPath);
if (!epubBook.isValid ())
QFAIL ("Test book is not valid: epub format");
QCOMPARE (epubBook.TitleInfo_.Title_, QString ("book-title1 n-1"));
QCOMPARE (epubBook.TitleInfo_.TextLanguage_, QString ("en"));
QCOMPARE (epubBook.TitleInfo_.Authors_.count (), 3);
QCOMPARE (epubBook.TitleInfo_.Authors_.at (0).FirstName_, QString ("First-Name1 Middle-Name1 Last-Name1 Nickname1"));
QCOMPARE (epubBook.TitleInfo_.Authors_.at (1).FirstName_, QString ("First-Name2 Middle-Name2 Last-Name2 Nickname2"));
QCOMPARE (epubBook.TitleInfo_.Authors_.at (2).FirstName_, QString ("First-Name3 Middle-Name3 Last-Name3 Nickname3"));
QCOMPARE (epubBook.TitleInfo_.Translators_.count (), 1);
QCOMPARE (epubBook.TitleInfo_.Translators_.at (0).FirstName_, QString ("First-Name4 Middle-Name4 Last-Name4 Nickname4"));
QCOMPARE (epubBook.TitleInfo_.Tags_.count (), 3);
QCOMPARE (epubBook.TitleInfo_.Tags_.at (0), QString ("architecture"));
QCOMPARE (epubBook.TitleInfo_.Tags_.at (1), QString ("art"));
QCOMPARE (epubBook.TitleInfo_.Tags_.at (2), QString ("art_instr"));
QCOMPARE (epubBook.PublishInfo_.ISBN_, QString ("isbn1"));
QCOMPARE (epubBook.PublishInfo_.Publisher_, QString ("publisher1"));
QCOMPARE (epubBook.PublishInfo_.PublishedYear_, 1900);
}
};
parsermanagertest.h
// Тут у нас всего две строки
#include "parsermanagertest.h"
QTEST_MAIN (TestParserManager)
Благодаря тестам я нашел с пяток ошибок в своих парсерах. Это очень мне помогло. Плюс это сподвигло меня на генерацию тестовых книг (изначально я использовал реальные). Чем тестовые лучше? Тем, что я их генерировал из fb2.xsd и на выходе получил все возможные варианты параметров. Для тестов лучше некуда.
Для fb2 формата я легко нашел генератор xml из схемы. Сама схема
тут. Плюс еще 3 дополнительные схемы для
жанров,
языка и
ссылок в fb2. Когда я начал искать способ генерации из этого xml, то первым делом пытался сделать через online и у меня ничего не получилось. Онлайн парсеры не смогли работать со внешними схемами. Поэтому я начал искать десктопный вариант. И им стала MSVS 2010. Она просто замечательно работает с xml схемами и замечательно генерирует из нее xml. Это я и поспешил сделать. Тестовая книга для fb2 была готова.
Теперь надо было сделать тестовую книгу для EPUB. И тут я столкнулся со сложностью: нормальной схемы я не смог найти. То, что нашел, не позволяло создавать документы. В итоге убив кучу времени я сделалход конем: сгенерил из своего тестового fb2 epub книжку и ее использовал в качестве лакмусовой бумажки. Получилось очень даже неплохо, как мне кажется.
После всех трудов я поделился этой новостью с людом и мне сказали, что на самом деле unit тесты нужны не для поиска ошибок и прочего, а для проверки функций после рефакторинга. Но мы то с Вами знаем, что никто рефакторингом никогда не занимается, потому что на это нету времени/денег/желания...
А я вероятнее всего продолжу использовать тесты. Мне понравилось. Тем более я страдаю желанием переписывать свой старый код, потому что он говно.
.Posted via
LeechCraft Blogique.