Управляемые данными юнит-тесты с Google Test

В настоящее время я пишу модульные тесты для встроенного приложения, используя Система модульного тестирования Google. Теперь мой начальник расстроился из-за того, что данные, которые я тестирую (т. Е. Значения, с которыми я вызываю методы тестируемого класса), жестко связаны в тестах. Он просит записать эти данные из файла. Его аргумент состоит в том, что, таким образом, было бы легче добавить еще один тест для углового случая, который был ранее забыт. Я не настолько опытен с юнит-тестами, но до сих пор я так не поступал. Поэтому я попытался выяснить, как лучше всего это сделать, даже если это вообще хорошая идея. Я быстро наткнулся на подход ДДТ (тестирование на основе данных).

В фреймворке модульного тестирования Google есть функция, которую они называют «Тесты со значениями параметров«. С этим мой тестовый прибор становится классом шаблона, и я могу передавать параметры. Однако я вижу некоторые проблемы с этим:

  • В настоящее время у меня есть приспособление для каждого тестируемого класса. Но мне нужно приспособление для каждого тестируемого метода, поскольку каждый метод требует различного набора параметров. Это будет много дополнительной работы.
  • Насколько я понимаю, я могу передать только один параметр. Поскольку для моих тестов мне нужно несколько (все параметры для моего метода плюс ожидаемые результаты), мне потребуется передать что-то вроде вектора или карты. Опять же, эта конструкция и поиск звучат как большая работа.

Я бы вообразил, что что-то настолько зрелое, как тестовая среда Google, чтобы упростить его. Тем не менее, они пишут

Параметризованные по значению тесты пригодятся [когда] вы хотите протестировать свой код на различных входах (например, тестирование на основе данных). Эту функцию легко использовать, поэтому, пожалуйста, проявите здравый смысл при ее использовании!

Кроме того, существует этот блог TotT: управляемые данными ловушки, также предупреждает меня о (злоупотреблении) модульных тестах, управляемых данными.

Итак, мой вопрос сводится к:

  • Стоит ли проводить модульные тесты, управляемые данными?
  • Как проводить управляемые данными юнит-тесты с помощью google test framework

На самом деле я не привязан к googletest и могу свободно выбирать любые фреймворки, которые мне нравятся.

РЕДАКТИРОВАТЬ

Я нашел следующее утверждение в записи часто задаваемых вопросов о Googletest.

В Google Test пока нет хорошей поддержки […] тестов, управляемых данными. Мы надеемся, что скоро сможем улучшить эту область.

3

Решение

GTest имеет поддержку для этого — но, возможно, они не знают …

использование testing::ValuesIn — как в этом упрощенном примере:

class SomeTests : public TestWithParam<int>
{
public:

};

TEST_P(SomeTests, shouldBePositive)
{
ASSERT_GT(GetParam(), 0);
}

И — способ, как получить значения из входного потока:

std::ifstream inputValuesFromFile("input.txt");
INSTANTIATE_TEST_CASE_P(FromFileStream,
SomeTests,
ValuesIn(std::istream_iterator<int>(inputValuesFromFile),
std::istream_iterator<int>()));

Чтобы использовать более сложные типы, чем «int», вам нужно написать оператор >> для него — например:

struct A
{
int a;
std::vector<int> b;
}
std::istream& operator >> (std::istream& is, A& out)
{
std::size_t bSize;
if ((is >> A.a) && (is >> bSize))
{
out.b.reserve(bSize);
while (bSize-- > 0)
{
int b;
if (!(is >> b))
break;
out.b.push_back(b);
}
}
return is;
}

Конечно, в более сложных случаях рекомендуется использовать XMl-подобные форматы (json?) И некоторые более специализированные итераторы, чем std::istream_iterator<T>,


Для XML — подобных форматов — вы можете рассмотреть такую ​​схему (это очень гипотетический код — у меня нет такой библиотеки в моей голове):

SomeLib::File xmlData("input.xml");

class S1BasedTests : public TestWithParam<S1>
{};

TEST_P(S1BasedTests , shouldXxxx)
{
const S1& s1 = GetParam();
...
}
auto s1Entities = file.filterBy<S1>("S1");
INSTANTIATE_TEST_CASE_P(S1,
S1BasedTests,
ValuesIn(s1Entities.begin(), s1Entities .end()));

// и т. д. для любых типов S1, которые вы хотите


Если на рынке нет такой C ++ — дружественной к типу библиотеки (я искал 2 минуты и не нашел) — тогда может быть что-то вроде этого:

SomeLib::File xmlFile("input.xml");

struct S1BasedTests : public TestWithParam<SomeLib::Node*>
{
struct S1 // xml=<S1 a="1" b="2"/>
{
int a;
int b;
};
S1 readNode()
{
S1 s1{};
s1.a = GetParam()->getNode("a").getValue<int>();
s1.b = GetParam()->getNode("b").getValue<float>();
return s1;
}
};

TEST_P(S1BasedTests , shouldXxxx)
{
const S1& s1 = readNode();
...
}
INSTANTIATE_TEST_CASE_P(S1,
S1BasedTests ,
ValuesIn(xmlFile.getNode("S1").getChildren()));
// xml=<S1s> <S1.../> <S1.../> </S1>

// и т. д. для любых типов узлов, таких как S1

3

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector