Откуда загружать данные-заглушки при модульном тестировании

Для целей модульного тестирования мне нужно смоделировать сетевой ответ. Ответ обычно представляет собой поток байтов, хранящийся в виде const vector<uint8_t>, Однако для модульного теста я хотел бы создать вектор с данными, которые либо жестко закодированы в файле CPP, либо считаны из файла в том же решении. Мой пример данных составляет около 6 кб. Каково общее руководство о том, где размещать данные при использовании googletest?

2

Решение

Возможно (а) вам требуется большая последовательность данных для некоторой роли, в которой
тесты будут просто читать. Это могут быть (класс) глобальные данные, с
const доступ.

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

Возможно оба. В любом случае обычная реализация GoogleTest будет
использовать испытательный стенд
инкапсулировать получение данных, будет приобретать его в
реализация виртуального светильника Setup() функция-член и
получить доступ к нему через метод получения прибора.

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

#include <vector>
#include <fstream>
#include <stdexcept>
#include "gtest/gtest.h"
class foo_test : public ::testing::Test
{
protected:
virtual void SetUp() {
std::ifstream in("path/to/case_data");
if (!in) {
throw std::runtime_error("Could not open \"path/to/case_data\" for input");
}
_case_data.assign(
std::istream_iterator<char>(in),std::istream_iterator<char>());
if (_global_data.empty()) {
std::ifstream in("path/to/global_data");
if (!in) {
throw std::runtime_error(
"Could not open \"path/to/global_data\" for input");
}
_global_data.assign(
std::istream_iterator<char>(in),std::istream_iterator<char>());
}
}
// virtual void TearDown() {}
std::vector<char> & case_data() {
return _case_data;
}
static std::vector<char> const & global_data() {
return _global_data;
}

private:
std::vector<char> _case_data;
static std::vector<char> _global_data;

};

std::vector<char> foo_test::_global_data;

TEST_F(foo_test, CaseDataWipe) {
EXPECT_GT(case_data().size(),0);
case_data().resize(0);
EXPECT_EQ(case_data().size(),0);
}

TEST_F(foo_test, CaseDataTrunc) {
EXPECT_GT(case_data().size(),0);
case_data().resize(1);
EXPECT_EQ(case_data().size(),1);
}

TEST_F(foo_test, HaveGlobalData) {
EXPECT_GT(global_data().size(),0);
}int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

Для случая (а) вы также можете рассмотреть возможность получения данных в глобальная настройка
функция-член путем подклассов ::testing::Environmentно я не вижу
общая причина для того, чтобы сделать это таким образом.

…Или это жесткий код?

Тогда к вопросу о том, нужно ли вообще хранить данные теста в файле или жесткий код
это в тестовом источнике. Читатели, которые счастливы в этом пункте, будут только скучать отныне.

Как общая проблема, это вопрос суждения при обстоятельствах, и я не думаю,
что использование googletest существенно снижает весы. Я думаю главное соображение
является: Желательно ли иметь возможность варьировать элемент тестовых данных без
перестроить тестовый набор?

Скажем, перестройка набора тестов для изменения этого элемента — немалая цена, и вы
предвидеть, что содержание предмета в будущем будет меняться независимо
соответствующего тестового кода. Или это может варьироваться, независимо от связанного тестового кода,
для разных конфигураций тестируемой системы. В этом случае лучше всего получить
элемент из файла или другого источника, который может быть выбран с помощью параметров времени выполнения
тестовый набор. В googletest подклассы class ::testing::Environment это
разработанное средство для параметризованного получения ресурсов тестового набора.

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

Если содержимое элемента данных теста является прочно связаны с
связанный тестовый код, то я склонен жестко кодировать его, а не извлекать его
из файла данных. Просто предвзятый, не догматически преданный. Может быть, ваш тест
Пакет использует надежные библиотечные средства для инициализации общедоступных тестовых данных API,
скажем, XML-файлы, которые также подключены к управлению тестами и дефектами,
системы. Отлично!

Я считаю совершенно желательным, чтобы файл тестовых данных был основным
Тестовый ресурс — тот, который не может сгенерировать тестовый набор — затем его содержимое
лучше всего быть текстовые данные, которые компетентный сопровождающий может легко понять
и манипулировать. В этом случае я, конечно, считаю, что
список шестнадцатеричных констант C / C ++, например, текстовые данные — это
исходный код. Если тестовый файл содержит двоичные данные или данные, ориентированные на машины
тогда набор тестов лучше всего содержал средства его производства из разборчивых
первичные ресурсы. Иногда набор тестов неизбежно зависит от
«архетипические» двоичные файлы из внешних источников, но они почти неизбежно влекут за собой
мрачное зрелище инженеров-испытателей и специалистов по исправлению ошибок, серых перед шестигранными редакторами.

Учитывая принцип, согласно которому первичные данные испытаний должны быть понятны сопровождающим, мы
можно считать нормой, что первичные тестовые данные будут «своего рода кодом»:
будьте свободны от логики, но это будет своего рода текстовые вещи, которые программисты
привыкли к съемке и редактированию.

Представьте себе, что конкретная последовательность из 4096 64-разрядных целых чисел без знака
(Большой волшебный стол) требуется для тестирования вашего программного обеспечения и плотно
встроенный в соответствующий тестовый код. Это может быть жестко закодировано как огромный вектор или массив
список инициализаторов в некотором исходном файле набора тестов. Возможно
Извлеченный набором тестов из файла данных, сохраненного в формате CSV или в
CSV-пунктуальные линии.

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

Но обеим этим точкам можно противопоставить наблюдение, что определяющие
Декларация BMT может быть закодирована в исходном файле самостоятельно. Это
может быть политикой проверки кода для набора тестов, который инициализирует данные теста
должен быть так закодировано — и, возможно, в файлах, которые придерживаются отличительного имени
условность. Конечно, фанатичная политика, но не более фанатичная, чем
тот, который настаивал бы, что все инициализаторы тестовых данных должны быть извлечены из файлов.
Если сопровождающий обязан исследовать BMT в любом файле, в котором он содержится,
не имеет значения, является ли расширение файла .cpp, .dat или же
что угодно: все дело в понятности «кода».

Для жесткого кодирования и против извлечения из файла данных, это может быть рекомендовано
что извлечение из файла данных должно вводить источник не имеет значения
возможные сбои в тестовых случаях — все Не должно случиться ошибки, которые
может победить чтение правильных данных из файла. Это накладывает накладные расходы на
разработка тестов для проведения правильного и четкого различия между подлинными неудачами тестов и
не удается получить данные испытаний из файла и четко диагностировать все возможные
причины последнего.

В случае googletest и сравнительно функциональных фреймворков этот момент может быть
до некоторой степени противодействовал, обращаясь к базовым классам полиморфного приспособления
лайк ::testing::Test а также ::testing::Environment, Это облегчает
разработчик тестов для инкапсуляции получения тестовых ресурсов в тест-кейсе
или инициализация набора тестов, так что все кончено, либо успешно, либо
с диагностированной ошибкой, до того, как будут выполнены тестовые задания любого теста.
RAII может поддерживать беспроблемное разделение между сбоями установки и реальными сбоями.

Тем не менее, существует неснижаемая нагрузка на обработку файла данных
маршрут а также есть эксплуатационные накладные расходы, что особенности RAII структуры
не делать ничего, чтобы уменьшить. В моих отношениях с здоровенными тестовыми системами, которые торгуют на
файлы данных, файлы данных просто более подвержены операционным неудачам, чем
исходные файлы, которые должны присутствовать и исправляться только во время сборки.
Файлы данных чаще всего оказываются отсутствующими или неуместными во время выполнения, или
содержащие искаженные материалы, или каким-то образом получить отказ в разрешении, или
как-то появиться при неправильной ревизии. Их использование в тестовой системе
не так просты или жестко управляются, как исходные файлы. вещи
Этого не должно быть
происходит для проверки файлов данных является частью оперативного трения
Тестовые системы, которые полагаются на них и пропорциональны их количеству.

поскольку исходные файлы может инкапсулировать инициализацию тестовых данных гигиенически
для отслеживания ревизий жесткое их кодирование можно приравнять к извлечению из
файл, с препроцессором, выполняющим извлечение как побочный продукт компиляции.
В этом свете зачем использовать другой механизм с дополнительными обязательствами для его извлечения?
Там могут быть хорошие ответы, такие как предложенный интерфейс XML с управлением тестами,
системы управления дефектами, но «это тестовые данные, так что не пишите их жестко» не очень хорошая.

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

До сих пор я не оспаривал аргумент revision-tracking-hygeine
для разделения на основе файлов инициализаторов тестовых данных. Я только что сделал
отметьте, что обычные исходные файлы, в которых инициализаторы жестко запрограммированы, могут
выполнить эту сегрегацию. И я не хочу оспаривать этот аргумент, но
Я хочу прекратить фанатичный вывод о том, что инициализаторы тестовых данных
должен в принципе всегда извлекаться из выделенных файлов —
будь то исходные файлы или файлы данных.

Нет необходимости объяснять причины, по которым можно противостоять этому выводу. Сюда
лежит тестовый код, который локально Меньше понятно, чем в среднем едят пиццу
программист будет писать и организации тестовых наборов файлов, которые растут
ошеломляет гораздо быстрее, чем необходимо или полезно. нормативном
все первичные ресурсы набора тестов — это «своего рода код».
набор навыков программиста включает в себя умение разбивать код на файлы
с соответствующей детализацией, чтобы обеспечить соответствующую гигиену отслеживания изменений.
Это не механическая процедура, это экспертиза для обзора кода.
Проверка кода может и должна обеспечивать инициализацию тестовых данных, однако
они выполнены, хорошо спроектированы и обработаны для отслеживания изменений
так же, как и во всех других рутинных отношениях.

Итог: если вы хотите иметь возможность запускать одну и ту же сборку тестового набора для множества
из этих ложных сетевых ответов прочитайте его из файла. Если с другой стороны это
инвариантен или ковариантен с конфигурациями сборки набора тестов, почему бы и нет жесткий
код это?

2

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

(Предостережение — этот ответ является общим для любой среды модульного тестирования)

Я предпочитаю хранить файлы тестовых данных как отдельные объекты в системе контроля версий. Это обеспечивает следующие преимущества:

  • Вы можете написать кодовый тест, чтобы принять любой или несколько файлов данных для тестирования различных ситуаций.
  • вы можете отслеживать изменения в данных по мере необходимости

Если вы не хотите, чтобы выполнение модульного теста читало файл данных, что может быть необходимым условием в некоторых ситуациях, вы можете написать программу или скрипт, который генерирует код C ++, который инициализирует вектор при настройке прибора.

1

По вопросам рекламы [email protected]