- PVSM.RU - https://www.pvsm.ru -
Хотя и существуют уже библиотеки для юнит-тестирования кода на С++, например, Google Test [1] или Bandit [2], но они написаны не мной здесь оно, на мой взгляд, как-то переусложнено, по сравнению с тем же JS. Там просто делаешь, например, npm i mocha assert --save-dev
и можно приступать к написанию тестов, а здесь же нужно это сделать ручками, а в случае с gtest
еще и собрать с помощью cmake
ее. Bandit подключается просто, но не умеет в сериализацию результатов в какой-то формат данных, gtest
это умеет, но его нужно собирать отдельно. А я не хочу выбирать "либо то, либо это". Мне было нужно сделать удобный и простой инструмент под мои задачи. Я хотел получить простую библиотеку без зависимостей, header-only, на несколько файлов, которую можно легко и быстро подключить к своему проекту, удобно внести в нее изменения (если это будет необходимо). Но, самое основное, мне хотелось получать удобные, машиночитаемые отчеты, причем не только в stdout
(или xml
, как в gtest
), но и в любой другой формат, который я захочу. Далее под катом.
Как я уже писал выше, библиотека dock [3] header-only, а значит ее подключение максимально простое:
#include <iostream>
#include <dock/dock.hpp>
using namespace dock;
int main() {
core().run();
return 0;
}
При сборке, например, в gcc, нужно передать только путь к папке с библиотеками и указать стандарт языка C++14. Я намеренно делаю так, потому что новые проекты я пишу на свежем стандарте, а для поддержки старых есть уже свои готовые библиотеки.
Описание тестов тоже сделано предельно простым:
using namespace dock;
Module(u8"Some module 1", [](DOCK_MODULE()) {
Test(u8"Some test 1", []() {
Assert::isTrue([]() -> bool { return true; });
});
Test(u8"Some test 2", []() {
Assert::isTrue([]() -> bool { return true; });
});
});
Module(u8"Some module 2", [](DOCK_MODULE()) {
Test(u8"Some test 1", []() {
Assert::isTrue([]() -> bool { return true; });
});
Test(u8"Some test 2", []() {
Assert::isTrue([]() -> bool { return false; });
});
});
Для удобства тесты группируются в модули. В них передается объект std::function<void(Module*)>
, внутри которого описываются непосредственно тесты. Тесты имеют примерно такой же синтаксис, только функциональный объект без параметров. Пока что я не делал проверку на уникальность имени модуля или теста, потому что это было не критично.
"Библиотека" Assert
содержит простой набор методов isTrue
,isEquals
, isGreater
, isLess
, которые по умолчанию могут сравнивать объекты через операторы ==
, >
или <
. Если операторов нет, то можно функцию сравнения передать в конце параметром (например, в виде лямбды).
static void isTrue(std::function<bool()> fcn);
template<typename T>
static void isEquals(const T a, const T b, std::function<bool(const T, const T)> compareFcn = defaultEqualsFunction<T>);
template<typename T>
static void isGreater(const T a, const T b, std::function<bool(const T, const T)> compareFcn = defaultGreaterFunction<T>);
template<typename T>
static void isLess(const T a, const T b, std::function<bool(const T, const T)> compareFcn = defaultLessFunction<T>);
А теперь как раз то, что было нужно мне: удобное преобразование результатов тестирования в необходимый формат данных. Для начала, просто хочется поработать с статистикой ведения проекта, смотреть динамику по тестам и подобные вещи, и мне это удобно делать на JS. Поэтому первый формат, который мне потребовался — JSON. В репозитории есть уже три готовых сериализатора: в JSON, в plain text и вывод в консоль с подсветкой. Использование сериализаторов очень простое:
nlohmann::json outJson;
JsonSerializer serializer(outJson, 4);
core().run();
core().collect(serializer);
std::cout << serializer << std::endl;
А сам интерфейс сериализатора выглядит следующим образом:
class ResultSerializer {
public:
virtual ~ResultSerializer() = default;
virtual void serialize(std::vector<Result>& results) = 0;
virtual std::string toString() const = 0;
friend std::ostream& operator<<(std::ostream& os, ResultSerializer& s);
};
Т.е. выводить результат можем куда угодно, подставить только std::ostream
и все. Логика работы сериализатора следующая:
collect()
и он вызывает метод serialize()
с вектором результатов.<<
вызывается метод toString()
, который выдает строку в std::ostream
.serialize()
сразу создаем нужную строку, а ее потом либо просто возвращаем, либо сохраняем ссылку на результаты и генерируем выдачу непосредственно при выдаче в ostream. В любом случае, остается свобода движения — движок выдает просто std::vector<dock::Result>
, а что с ним делать уже дело ваше :).Лицензия свободная (MIT), потому что мне не жалко и будет приятно видеть её использование. Для сериализаторов использовались библиотеки termcolor [4] и JSON for Modern C++ [5], но можно спокойно убрать их вместе с ненужными сериализаторами.
Автор: emdc
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-3/234798
Ссылки в тексте:
[1] Google Test: https://github.com/google/googletest
[2] Bandit: https://github.com/banditcpp/bandit
[3] dock: https://github.com/emdc/dock
[4] termcolor: https://github.com/ikalnytskyi/termcolor
[5] JSON for Modern C++: https://github.com/nlohmann/json
[6] Источник: https://habrahabr.ru/post/319858/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.