В настоящее время я пытаюсь сгенерировать некоторое UT пост-кодирование для модуля.
Модуль классифицирует входные данные в БД, если совпадение найдено, возвращается правильное значение.
После составления таблицы со всеми возможными входными сценариями и ожидаемыми результатами я обнаружил, что мне нужно создать более 50 тестов.
Поскольку все тесты в основном одинаковы, за исключением названия теста и входных данных, казалось, что здесь подходит какой-то шаблон, уменьшающий путаницу кода.
То, что я изначально представлял, было либо макросом, который создает такой шаблон и расширяется до тестов с входными данными и ожидаемым результатом, ИЛИ использует структуру, представляющую таблицу сценариев с некоторой помощью макроса, чтобы обернуть его.
(Я думаю, что шаблон C ++ также подходит здесь).
Тем не менее, я не уверен, что это правильный путь, и хотел бы знать, если кто-нибудь может дать некоторые рекомендации, как справиться с этим.
Существует также вопрос о том, как это соответствует подходу TDD.
Вот идея использования CppUTest:
#define GEN_TEST_MOD_CLASSIFIER(GROUP_NAME, TEST_NAME, PRIORITY, isCOMPRESS, isX, EXPECTING) \
TEST(GROUP_NAME, TEST_NAME) \
{ \
int hit; \
setupDB(PRIORITY, isCOMPRESS, isX); \
hit = func(PRIORITY, isCOMPRESS, isX); \
CHECK_EQUAL(EXPECTING, hit); \
}
Пример использования:
GEN_TEST_MOD_CLASSIFIER(Classifier_Tests, LowPriority_NoCompress_NoX__HIT, PRIO_LOW, NOT_COMPRESS, NO_X, HIT_DB)
Спасибо,
Эди.
Что ж, без внешних инструментов лучше всего использовать макросы. Это потому, что вам нужно иметь TEST(GROUP_NAME, TEST_NAME)
для каждого теста.
Это может быть не так. Вам может быть хорошо, если у вас нет отдельного контрольного примера для каждого сценария, или CppUTest может поддерживать программный способ добавления контрольных примеров.
В этом случае вы можете создать метод, который принимает вектор кортежей input-output-testcasename. И будет добавлять тестовые случаи / запускать тесты на каждом кортеже. Макросы не нужны.
Примерно так (псевдокод):
typedef std::tuple<std::string, std::string, PriorityType, CompressType, ExpectedValueType> TestInfo;
void RunTest(const TestInfo& testInfo)
{
// Assuming here you're OK with this kind of test cases separation
std::cout << "Running test" << std::get<0>(testInfo) << ", " << std::get<1>(testInfo) << std::endl;
int hit;
setupDB(std::get<2>(testInfo), std::get<3>(testInfo), isX);
hit = func(std::get<2>(testInfo), std::get<3>(testInfo), isX);
CHECK_EQUAL(std::get<4>, hit);
}
void RunTests(const TestInfo& testInfo)
{
std::for_each(testInfo.begin(), testInfo.end(), RunTest);
}
std::vector<TestInfo> tests = { test1, test2 };
RunTests(tests);
Если это не сработает, макросы на самом деле хороши, и вы также можете использовать табличный подход. Посмотрите на Boost препроцессор (http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/index.html)
Снова псевдокод:
#include <boost\preprocessor\seq.hpp>
#define GENERATE_TEST_CASE(_ignore1_, _ignore2_, _testCaseInfoSequence_) \
TEST(BOOST_PP_SEQ_ELEM(0, testCaseInfoSequence), BOOST_PP_SEQ_ELEM(1, testCaseInfoSequence)) \
{ \
int hit; \
setupDB(BOOST_PP_SEQ_ELEM(2, testCaseInfoSequence), BOOST_PP_SEQ_ELEM(3, testCaseInfoSequence), BOOST_PP_SEQ_ELEM(4, testCaseInfoSequence)); \
hit = func(BOOST_PP_SEQ_ELEM(2, testCaseInfoSequence), BOOST_PP_SEQ_ELEM(3, testCaseInfoSequence), BOOST_PP_SEQ_ELEM(4, testCaseInfoSequence)); \
CHECK_EQUAL(BOOST_PP_SEQ_ELEM(5, testCaseInfoSequence), hit); \
}
#define TESTCASES \
( \
(Group1)(Test1)(prio1)(isCompress1)(isX1)(expectedVal1) \
(Group1)(Test2)(prio2)(isCompress2)(isX2)(expectedVal2) \
)
// This statement will generate all tests cases in defined in TESTCASES sequnce.
BOOST_PP_SEQ_FOREACH(GENERATE_TEST_CASE, _ignore_, TESTCASES);
Других решений пока нет …