Что ж, предпочтительный путь в C ++ для файлового ввода-вывода - потоковый. Таким образом, в приведенном выше примере было бы гораздо разумнее добавить поток вместо имени файла. Например,
Foo(const std::stream& file) : file_(file)
Таким образом, вы можете использовать std::stringstream
для модульного тестирования и иметь полный контроль над тестом.
Если вы не хотите использовать потоки, то можно использовать стандартный пример шаблона RAII, определяющего класс File
. В таком случае «простой» способ - создать чистый класс виртуального интерфейса File
, а затем реализацию интерфейса. Класс Foo
будет затем использовать интерфейсный класс File. Например,
Foo(const File& file) : file_(file)
Затем выполняется тестирование, просто создав простой подкласс для File
и вставив его вместо этого (заглушка). Также можно создать фиктивный класс (см., Например, Google Mock).
Однако вы, вероятно, хотите провести модульное тестирование и класса реализации File
, и, поскольку это RAII, он, в свою очередь, требует некоторого внедрения зависимостей. Я обычно пытаюсь создать чистый класс виртуального интерфейса, который просто обеспечивает базовые операции с файлами C (открытие, закрытие, чтение, запись и т. Д. Или fopen, fclose, fwrite, fread и т. Д.). Например,
class FileHandler {
public:
virtual ~FileHandler() {}
virtual int open(const char* filename, int flags) = 0;
// ... and all the rest
};
class FileHandlerImpl : public FileHandlerImpl {
public:
virtual int open(const char* filename, int flags) {
return ::open(filename, flags);
}
// ... and all the rest in exactly the same maner
};
Этот класс FileHandlerImpl
настолько прост, что я не тестирую его модульно. Тем не менее, преимущество заключается в том, что используя его в конструкторе класса FileImpl
, я могу легко выполнить модульное тестирование класса FileImpl
. Например,
FileImple(const FileHandler& fileHandler, const std::string& fileName) :
mFileHandler(fileHandler), mFileName(fileName)
Единственный недостаток на данный момент заключается в том, что FileHandler
нужно обойти. Я думал об использовании интерфейса FileHandle
для предоставления статических экземпляров set / get-методов, которые можно использовать для получения одного глобального экземпляра объекта FileHandler
. Хотя на самом деле это не единичный и, следовательно, тестируемый модуль, это не элегантное решение. Я думаю, что передача обработчика - лучший вариант прямо сейчас.