使用 TDD 时隐藏文件访问实现细节
Hide file access implementation details while using TDD
我正在尝试创建一个继承自 TableFileInterface 的 CsvTableFile class。为了对 class 进行单元测试,我想模拟所有文件访问。
这里的问题是:为了能够模拟文件访问,我必须引入一个 WinApiInterface(或者任何你想调用它的东西,比如 FileIoInterface),它必须提供给 CsvTableFile 的构造函数 class.然而,这向所有人展示了致力于 OpenFile 的实现。如果我想将内部结构从 OpenFile 更改为 ifstream,我必须更改构造函数,因为那时我不需要 WinApiInterface 传递给 class,而是 FileStreamInterface。
我对这种情况不是特别满意,但我想不出解决办法。我错过了什么吗?
class WinApiInterface {
public:
virtual HFILE WINAPI OpenFile(
_In_ LPCSTR lpFileName,
_Out_ LPOFSTRUCT lpReOpenBuff,
_In_ UINT uStyle ) = 0;
}
class WinApi : public WinApiInterface {
public:
virtual HFILE WINAPI OpenFile(
_In_ LPCSTR lpFileName,
_Out_ LPOFSTRUCT lpReOpenBuff,
_In_ UINT uStyle ) {
return ::OpenFile(lpFileName, lpReOpenBuff, lpReOpenBuff);};
}
class TableFileInterface {
public:
virtual int Open(std::string file) = 0;
virtual int Close() = 0;
virtual std::string GetCellAsString(size_t row; size_t column) = 0;
virtual double GetCellAsDouble(size_t row; size_t column) = 0;
}
class CsvTableFile : public TableFileInterface {
public:
CsvTableFile(const WinApiInterface& win_api)
: win_api_(win_api){};
~CsvTableFile(){};
virtual int Open(std::string file) { *CODE USING win_api_.OpenFile(...)*};
virtual int Close() {...};
std::string GetCellAsString(size_t row; size_t column) {...};
double GetCellAsDouble(size_t row; size_t column) {...};
protected:
WinApiInterface win_api_;
}
如果您的主要目标是避免 CsvTableFile
需要更改,您可以将其模板化并将打开逻辑转移到模板参数对象。
template <class T>
class CsvTableFile : public TableFileInterface {
public:
CsvTableFile(const T& _fileopener):fileopener(_fileopener){}
int Open(std::string file) { fileopener.OpenFile(...); }
T& fileopener;
...
};
然后为您要使用的每种文件处理类型创建一个 class,并在每个 classes 中实现一个 OpenFile
方法。
我正在尝试创建一个继承自 TableFileInterface 的 CsvTableFile class。为了对 class 进行单元测试,我想模拟所有文件访问。
这里的问题是:为了能够模拟文件访问,我必须引入一个 WinApiInterface(或者任何你想调用它的东西,比如 FileIoInterface),它必须提供给 CsvTableFile 的构造函数 class.然而,这向所有人展示了致力于 OpenFile 的实现。如果我想将内部结构从 OpenFile 更改为 ifstream,我必须更改构造函数,因为那时我不需要 WinApiInterface 传递给 class,而是 FileStreamInterface。
我对这种情况不是特别满意,但我想不出解决办法。我错过了什么吗?
class WinApiInterface {
public:
virtual HFILE WINAPI OpenFile(
_In_ LPCSTR lpFileName,
_Out_ LPOFSTRUCT lpReOpenBuff,
_In_ UINT uStyle ) = 0;
}
class WinApi : public WinApiInterface {
public:
virtual HFILE WINAPI OpenFile(
_In_ LPCSTR lpFileName,
_Out_ LPOFSTRUCT lpReOpenBuff,
_In_ UINT uStyle ) {
return ::OpenFile(lpFileName, lpReOpenBuff, lpReOpenBuff);};
}
class TableFileInterface {
public:
virtual int Open(std::string file) = 0;
virtual int Close() = 0;
virtual std::string GetCellAsString(size_t row; size_t column) = 0;
virtual double GetCellAsDouble(size_t row; size_t column) = 0;
}
class CsvTableFile : public TableFileInterface {
public:
CsvTableFile(const WinApiInterface& win_api)
: win_api_(win_api){};
~CsvTableFile(){};
virtual int Open(std::string file) { *CODE USING win_api_.OpenFile(...)*};
virtual int Close() {...};
std::string GetCellAsString(size_t row; size_t column) {...};
double GetCellAsDouble(size_t row; size_t column) {...};
protected:
WinApiInterface win_api_;
}
如果您的主要目标是避免 CsvTableFile
需要更改,您可以将其模板化并将打开逻辑转移到模板参数对象。
template <class T>
class CsvTableFile : public TableFileInterface {
public:
CsvTableFile(const T& _fileopener):fileopener(_fileopener){}
int Open(std::string file) { fileopener.OpenFile(...); }
T& fileopener;
...
};
然后为您要使用的每种文件处理类型创建一个 class,并在每个 classes 中实现一个 OpenFile
方法。