Лично у меня прием, описанный ниже, даже уже набил оскомину, но от этого не сделался ни на каплю хуже.
Итак, функция, которая что-то делает с файлом:
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()
что-то там выводит про этот новый функционал.