如何创建无实际文件的std::filesystem::directory_entry用于测试?
解决std::filesystem directory_entry模拟的性能兼容问题
你遇到的核心问题是std::filesystem::directory_entry绑定真实文件系统状态,无法直接自定义属性,同时抽象封装会导致生产代码性能大幅下降。以下是两种兼顾性能和测试需求的解决方案:
方案1:C++20原生支持(最优解)
C++20为directory_entry新增了构造函数,允许直接传入自定义file_status,无需依赖真实文件:
// 构造自定义文件状态 fs::file_status regular_status; regular_status.type(fs::file_type::regular); regular_status.permissions(fs::perms::owner_read | fs::perms::owner_write); // 直接创建带自定义状态的directory_entry fs::directory_entry mock_entry("fake/path/file.txt", regular_status);
对于file_size这类需要文件系统查询的属性,若生产代码中是通过directory_entry::file_size()直接获取,可在测试中结合gmock的动作,将属性获取逻辑也抽象为provider的一部分(仅测试时替换,生产代码仍调用原生方法):
// 扩展directory_provider,添加属性获取接口 class directory_provider { public: virtual std::vector<fs::directory_entry> list_dir(const fs::path& path) = 0; virtual uintmax_t file_size(const fs::directory_entry& entry) = 0; }; // 生产实现直接调用原生方法 class filesystem_provider : public directory_provider { public: // ... 原有list_dir实现 uintmax_t file_size(const fs::directory_entry& entry) override { return entry.file_size(); } }; // Mock实现返回自定义大小 class mock_provider : public directory_provider { public: MOCK_METHOD(std::vector<fs::directory_entry>, list_dir, (const fs::path& path), (override)); MOCK_METHOD(uintmax_t, file_size, (const fs::directory_entry& entry), (override)); }; // 测试时设置预期 TEST(YourTest, MockFileSize) { mock_provider mock; fs::directory_entry mock_entry("fake/path", fs::file_status{fs::file_type::regular}); ON_CALL(mock, list_dir(testing::_)).WillByDefault(testing::Return({mock_entry})); ON_CALL(mock, file_size(testing::_)).WillByDefault(testing::Return(1024)); // 执行测试逻辑 }
这种方式生产代码仅增加了一层极薄的封装,性能损耗可忽略,同时测试完全脱离真实文件系统。
方案2:C++17兼容的临时文件模拟(无代码侵入)
如果项目仍使用C++17,可在测试中创建真实临时文件,构造符合要求的directory_entry后再清理:
TEST(YourTest, MockDirectoryListing) { mock_provider mock; // 创建临时目录和测试文件 auto temp_dir = fs::temp_directory_path() / fs::unique_path(); fs::create_directory(temp_dir); auto test_file = temp_dir / "test.bin"; // 设置文件大小为2048字节 std::ofstream(test_file).seekp(2047).put('\0'); // 构造真实的directory_entry fs::directory_entry file_entry(test_file); fs::directory_entry dir_entry(temp_dir); // 设置mock返回值 ON_CALL(mock, list_dir(testing::_)) .WillByDefault(testing::Return(std::vector<fs::directory_entry>{file_entry, dir_entry})); // 执行测试逻辑... // 清理临时文件 fs::remove_all(temp_dir); }
该方案生产代码零改动,完全保留原生性能,仅测试阶段有少量IO开销,适合对性能敏感的场景。
为什么抽象封装会导致性能下降?
你提到的directory_entry_pod或多态指针方案,本质是引入了额外的内存拷贝或虚函数调用。在遍历大型仓库(如Linux内核)时,这些开销会被海量文件操作放大,最终导致50%的性能损耗。因此,优先选择不修改生产代码结构的模拟方案。
内容的提问来源于stack exchange,提问作者Dominik Kaszewski
相关产品推荐
相关产品推荐

