Потоки в С++ для удобного тестирования

Лично у меня прием, описанный ниже, даже уже набил оскомину, но от этого не сделался ни на каплю хуже.

Итак, функция, которая что-то делает с файлом:

std::string DoSomethingWithFile(const std::string& name) {
  std::ifstream is(name);
  // ...
  return a_value;
}

Что плохого в этой функции? Для ее тестирования нужен реальный файл на файловой системе. В принципе, это не смертельно для unit-тестов, но как-то коряво, особенно если логика требует большого количества тестов.

Лично я взял себе за правило всегда разделять работу с файлом и его открытие:

std::string DoSomethingWithStream(std::istream* is) {
  // ...
  return a_value;
}

std::string DoSomethingWithFile(const std::string& name) {
  std::ifstream is(name);
  return DoSomethingWithStream(&is);
}

Тогда первую функцию можно в хвост и в гриву оттестировать, подсунув ей std::istringstream в тесте. А вторую, прикрыв глаза рукой, можно не тестировать или тестировать примитивно на одном реальном файле просто на предмет того, что она может его открыть.

Бонус

Еще одно мое собственное правило: в принципе стараться не использовать стандартные потоки std::cin/cout/cerr напрямую, а всегда передавать их как параметр. Например, есть фукнция usage(), которая выводит справку о программе. Если cout/cerr передать через параметр, то можно будет делать тесты на наличие определенных строк в выводе этой функции. Добавил новый функционал и наряду с прочими тестами добавил тест для проверки, что usage() что-то там выводит про этот новый функционал.


Disclaimer

Комментарии