4. Модульное тестирование
Цель работы
Проверить, что в случае непрохождения тестов автоматическая сборка завершается
со статусом failed.
Цель работы:
Реализовать движение всех фигур.
Общие сведения
Для покрытия тестами приложение декомпозируют на минимальные самодостаточные единицы. В процедурных языках программирования такая единица — функция.
Для покрытия функции тестами необходимо вызывать ее с заранее подготовленными входными параметрами и сравнить фактический результат ее работы с ожидаемым.
Код тестов должен быть написан отдельно от кода приложения. Поэтому для запуска тестов пишется отдельное приложение. Тестовое приложение обычно включает в себя автоматическую регистрацию новых тестов и формирование отчета. Для автоматизации процесса используют специальные библиотеки.
Сборка приложения и тестов
В предыдущей лабораторной работе мы разделили проект на консольное приложение и
статическую библиотеку. Артефакт сборки тестов — еще один исполняемый файл,
который линкуется со статической библиотекой библиотекой. Таким образом,
граф зависимостей для примера hello принимает вид (диаграмма сокращена):

Из графа зависимостей мы видим, что один и тот же код как используется в приложении, так и покрыт тестами.
При сборке нужно предусмотреть возможность конфликтов имен объектных файлов.
Например, при компиляции файлов src/main.c и test/main.c не должен
создаваться один и тот же объектный файл obj/main.o. Один из способов
предовращения таких конфликтов — дублирование структуры репозитория в каталоге
obj:
obj
|-- src
| |-- parser.o
| `-- main.o
`-- test
|-- parser_test.o
`-- main.o
Библиотека ctest
Примеры доступны по адресу: https://github.com/bvdberg/ctest.
Файл test/main.c:
#define CTEST_MAIN
#include <ctest.h>
int main(int argc, const char** argv)
{
return ctest_main(argc, argv);
}
Пример определения теста:
#include <sum.h>
#include <ctest.h>
CTEST(arithmetic_suite, simple_sum)
{
// Given
const int a = 1;
const int b = 2;
// When
const int result = sum(a, b);
// Then
const int expected = 3;
ASSERT_EQUAL(expected, result);
}
Здесь:
CTEST(<suite_name>, <test_name>)— макрос для создания и регистрации тестовой функции. Все определенные таким образом тесты запускаются автоматически при вызовеctest_main.ASSERT_EQUAL(<expected>, <real>)— макрос для сравнения ожидаемого результата с фактическим.
Библиотека ctest предоставляет следующие макросы:
CTEST(sname, tname)— макрос для определения теста.sname— имя набора тестов,tname— имя теста.CTEST_LOG(const char* fmt, ...)— запись в лог. Используется синтаксис аналогичныйprintf.ASSERT_STR(exp, real)— макрос для сравнения строк (``char* ``).ASSERT_EQUAL(exp, real)— сравнение переменных типаint.ASSERT_EQUAL_U(exp, real)— сравнение переменных типаunsigned.ASSERT_NOT_EQUAL(exp, real)— проверка на неравенство переменных типаint.ASSERT_NOT_EQUAL_U(exp, real)— проверка на неравенство переменных типаunsigned.ASSERT_DATA(exp, expsize, real, realsize)— сравнение массивов.ASSERT_INTERVAL(exp1, exp2, real)— проверка принадлежности числаrealинтервалу[exp1, exp2]. Все аргументы типа intASSERT_DBL_NEAR_TOL(exp, real, tol)— сравнение переменных типаdoubleс заданным допустимым отклонениемtol.ASSERT_DBL_NEAR(exp, real)— сравнение переменных типаdoubleсtol = 1e-4ASSERT_NULL(real)ASSERT_NOT_NULL(real)ASSERT_TRUE(real)ASSERT_FALSE(real)
Руководство
В структуру проекта, сформированную в предыдущей лабораторной работе, следует
добавить каталоги test и thirdparty. В результате получим:
.
|-- bin
| |-- chessviz
| `-- chessviz-test
|-- .clang-format
|-- .gitignore
|-- Makefile
|-- obj
|-- README.md
|-- src
|-- test
| |-- parser_test.c
| `-- main.c
`-- thirdparty
|-- .clang-format
`-- ctest.h
Для сторонних библиотек создан отдельный конфиг .clang-format. Его
содержимое:
---
DisableFormat: true
SortIncludes: false
...
Форматирование исходного кода сторонних библиотек усложняет их поддержку и
вендоринг новых версий, а в случае с ctest приводит к появлению
предупреждений на этапе компиляции.
Этапы работы
Каждый этап — отдельный коммит.
Добавить в репозиторий библиотеку ctest (заголовочный файл).
Добавить точку входа для запуска тестов —
test/main.c. Настроить сборку и запуск тестов (доработатьMakefile). Обычно цель по умолчанию используется только для компиляции приложения. Для компиляции и запуска тестов создают отдельную цель test. Таким образом, для полной сборки приложения и запуска тестов нужно выполнить команды:make make test
Написать любой простейший тест, проверить его работоспособность.
Реализовать функциональность в соответствии со своим вариантом.
Покрыть приложение тестами. Каждую группу тестов можно оформить в отдельный коммит.
Настроить запуск тестов в CI.