如果您考虑一下,您需要将 IFilesystem 的实例注入到您的类中的唯一原因是在测试中模拟它。无论如何,您的非测试代码库的任何部分都不会使用任何东西,但真正的文件系统,因此可以安全地假设您可以将类型注入到您的类中,并在您的代码库中自由使用它们而不会发生类型冲突。
所以,假设你有兴趣班
struct BuildTree
{
BuildTree(std::string_view dirname) { /*...*/ }
bool has_changed()
{
// iterates through files in directory
// recurs into directories
// checks the modification date against lastModified_
// uses boost::filesystem::last_write_time(), etc.
}
private:
boost::filesystem::file_time_type lastModified_;
};
现在,您可以将类型注入到类中。这种类型将是一个带有一堆静态方法的类。会有一个RealFilesystem
类型会重定向到 boost::filesystem 方法,并且会有一个SpyFilesystem
.
template <class Filesystem>
struct BuildTree
{
// ...
bool has_changed()
{
// uses Filesystem::last_write_time(), etc.
}
private:
typename Filesystem::file_time_type lastModified_;
};
这SpyFilesystem
将类似于 PIMPL 习惯用法,因为静态方法会将调用重定向到实际实现。
struct SpyFilesystemImpl;
struct SpyFilesystem
{
using file_time_type = typename SpyFilesystemImpl::file_time_type;
static file_time_type last_time_write(std::string_view filename)
{
return instance.last_time_write(filename);
}
// ... more methods
static SpyFilesystemImpl instance;
};
SpyFilesystemImpl SpyFilesystem::instance{};
// No warranty of completeness provided
struct SpyFilesystemImpl
{
using file_time_type = std::chrono::system_clock::time_point;
void create_directory(std::string_view path) { /*...*/ }
void touch(std::string_view filename)
{
++lastModified_[filename];
}
file_time_type last_time_write(std::string_view filename)
{
return std::chrono::system_clock::time_point{lastModified_[filename]};
}
private:
std::unordered_map<std::string, std::chrono::seconds> lastModified_;
};
最后,在您的每个测试中,您将准备一个 实例SpyFilesystemImpl
,将其分配给SpyFilesystem::instance
,然后使用 实例化您的类SpyFilesystem
。像这样
// ... our SpyFilesystem and friends ...
// Google Test framework
TEST(BuildTree, PickUpChanges)
{
SpyFilesystemImpl fs{};
fs.create_directory("foo");
fs.touch("foo/bar.txt");
SpyFilesystem::instance = fs;
BuildTree<SpyFilesystem> tree("foo");
EXPECT_FALSE(tree.has_changed());
SpyFilesystem::instance.touch("foo/bar.txt");
EXPECT_TRUE(tree.has_changed());
}
这种方法的优点是生成的二进制文件中没有运行时开销(前提是启用了优化)。但是,它需要更多样板代码,这可能是个问题。