admin 管理员组文章数量: 1086019
I have some code iterating over files using std::filesystem
. I am using dependency injection to mock out the directory_iterator
logic, but I have trouble constructing a mocked directory_entry
, which does not allow setting all the values and always sets them based on a real file under given path.
#include <gmock/gmock.h>
#include <filesystem>
namespace fs = std::filesystem;
class directory_provider {
public:
virtual std::vector<fs::directory_entry> list_dir(const fs::path& path) = 0;
};
class filesystem_provider : public directory_provider {
public:
std::vector<fs::directory_entry> list_dir(const fs::path& path) override {
return { fs::directory_iterator{ path }, fs::directory_iterator{} };
}
};
class mock_provider : public directory_provider {
public:
MOCK_METHOD(std::vector<fs::directory_entry>, list_dir, (const fs::path& path), (override));
};
Is there a way to solve this, allowing me to return a directory_entry
with arbitrary values such as file_size
and is_regular_file
. As it stands, I cannot even set arbitrary path without throwing a filesystem_error
with "No such file or directory".
I tried changing the return type to std::vector<directory_entry_pod>
or std::vector<std::unique_ptr<directory_entry_provider>>
, where the first simply copies all data into a struct I can easily fill in UT, while the other wraps directory_entry
into a polymorphic class I can mock. Unfortunately, I measured the conversions and/or polymorphism to decrease throughput of production code by as much as 50% (all optimized, testing recursively on large repos like Linux kernel).
I have some code iterating over files using std::filesystem
. I am using dependency injection to mock out the directory_iterator
logic, but I have trouble constructing a mocked directory_entry
, which does not allow setting all the values and always sets them based on a real file under given path.
#include <gmock/gmock.h>
#include <filesystem>
namespace fs = std::filesystem;
class directory_provider {
public:
virtual std::vector<fs::directory_entry> list_dir(const fs::path& path) = 0;
};
class filesystem_provider : public directory_provider {
public:
std::vector<fs::directory_entry> list_dir(const fs::path& path) override {
return { fs::directory_iterator{ path }, fs::directory_iterator{} };
}
};
class mock_provider : public directory_provider {
public:
MOCK_METHOD(std::vector<fs::directory_entry>, list_dir, (const fs::path& path), (override));
};
Is there a way to solve this, allowing me to return a directory_entry
with arbitrary values such as file_size
and is_regular_file
. As it stands, I cannot even set arbitrary path without throwing a filesystem_error
with "No such file or directory".
I tried changing the return type to std::vector<directory_entry_pod>
or std::vector<std::unique_ptr<directory_entry_provider>>
, where the first simply copies all data into a struct I can easily fill in UT, while the other wraps directory_entry
into a polymorphic class I can mock. Unfortunately, I measured the conversions and/or polymorphism to decrease throughput of production code by as much as 50% (all optimized, testing recursively on large repos like Linux kernel).
2 Answers
Reset to default 3Short answer: No, you cannot populate a directory_entry
with arbitrary data.
(Slightly) longer answer:
According to cppreference the directory_entry
class represents a directory entry. It also present few ways to actually modify the content of that class. After inspecting the few functions we can use to actually populate the class with data, we see that all of them only takes a path and all of them can fail on OS API error. This means that the only way to actually populate this class with the desired data, is to ensure that it actually is a valid directory_entry
(i.e. create that entry in the filesystem with the desired data and give the directory_entry
the path to that newly created filesystem entry).
While @Mestkon is correct that it is not possible to arbitrarily populate std::filesystem:directory_entry
, I did find a way to wrap it in a way which incurs very little compile time overhead and zero runtime overhead:
class unmocked {};
template <typename T>
class mocked : unmocked {};
template <typename T>
using mockable = std::conditional_t<
std::is_base_of_v<unmocked, mocked<T>>,
T,
mocked<T>
>;
#ifdef UNIT_TEST
template <typename T>
class mocked<std::filesystem::directory_entry> {
// Match T constructors, functions, operators
};
#endif
In production, without any specialization, mockable<T> == T
, so performance is not impacted. In UT, we can provide simple backing fields for every getter, or forward them to a gmock object. Note that MOCK_METHOD
makes an object non-copyable, so you might need to store a reference or smart pointer to the mock, depending on your needs.
本文标签: cCreate stdfilesystemdirectoryentry without backing files for testingStack Overflow
版权声明:本文标题:c++ - Create std::filesystem::directory_entry without backing files for testing - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://roclinux.cn/p/1744069922a2528298.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论