Модульные тесты на основе данных с помощью google test

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

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

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

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

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

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

Итак, мой вопрос сводится к следующему:

  • Это хорошая идея - проводить модульные тесты на основе данных?
  • Как выполнять модульные тесты на основе данных с помощью Google Test Framework

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

ИЗМЕНИТЬ

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

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


person sigy    schedule 27.10.2016    source источник


Ответы (1)


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

person PiotrNycz    schedule 28.10.2016
comment
Да, это функция тестов с параметризацией значений, о которой я говорил. Но это становится очень сложным, не так ли? Мне понадобится отдельный прибор для каждого тестируемого метода (из-за разных сигнатур). И для каждого прибора потребуется свой operator>> и структура. - person sigy; 28.10.2016
comment
Как я уже писал в своем ответе - поищите какую-нибудь xml-библиотеку для c ++ - чтобы получить istream из xml. Имея такой XML-парсер - определите параметризованный набор тестов для каждого вида XML-данных. Найдите такие вопросы, как этот stackoverflow .com / questions / 12338476 / или просто задайте другой вопрос о том, как получить различные структуры из одного файла - или иметь много файлов ... Я добавляю некоторые обновления в свой ответ - person PiotrNycz; 28.10.2016