I have a struct Region
, and I want to access its private members in tests like googletest. Now, I have some problems:
Region
's friend.There are some ways to mitigate this, however, they are not suitable in my case:
#ifdef DEBUG ... public: ... #else private:
. We don't do this for some good reasons.Region
into two parts, the new Region
holds a RegionImpl
. All tests that needs to access "private" members can test on RegionImpl
. However, it also has some limitations too.Now, I want to introduce the current solution of mine, and my question is totally based on this solution. The question is about how to address the drawback of my code. However, feel free to comment if you have a better idea.
The idea is that we can introduce a DebugRegion
that holds a Region
inside it, and it's Region
's friend. The DebugRegion
can be viewed as a smart pointer to Region
, so it can access all Region
's public members through operator->
.
However, when we need to access Region
's private members, we have to write a DEBUG_xxx
to delegate. So we can use debug_region.DEBUG_foo()
to access.
struct Region {
private:
void foo() {}
template <typaname T> bool bar(int z);
static std::string foobar();
private:
friend DebugRegion;
};
struct DebugRegion
{
DebugRegion(Region & r)
: region(r)
{}
Region * operator->() { return ®ion; }
Region * operator->() const { return ®ion; }
void DEBUG_foo() {
return region.foo();
}
template <typaname T> bool DEBUG_bar(int z) {
return region.bar(z);
}
static std::string DEBUG_foobar() {
return Region::foobar();
}
private:
Region & region;
};
Now comes my question, in fact, I think create a delegate function for every private members make the solution not perfect. Can we have a "magic" oeprator->
can even access Region
's private members here? I tried, but failed.
By intuition, I think maybe we should have some magic forward function to do so, however I can't come up with that function.
This is not ideal, but it does not require to add a DebugRegion
type. Your "magic operator->
" cannot be done, as far as I can tell, because friendship is not transferable.
You can add a function template to your class-to-be-tested but not implement it:
struct Region {
private:
void foo() {}
template <typaname T> bool bar(int z);
static std::string foobar();
public:
template <typename> void testRegion(); // you may want to make this const
};
Then in your gtest or whatever framework you use, you implement one testRegion
for each individual test. You can even use the class name of your current test-instance, but I'll keep the example simple:
// in RegionTest.cpp or similar
namespace {
struct tagRegionTestFoo {};
struct tagRegionTestBar {};
}
template <>
void Region::testRegion<tagRegionTestFoo>() {
// your test impl for test "foo" with private access
}
TEST(Region, foo) {
Region region{};
// public manipulation of region
region.testRegion<tagRegionTestFoo>();
}
template <>
void Region::testRegion<tagRegionTestBar>() {
// your test impl for test "bar" with private access
}
TEST(Region, bar) {
Region region{};
// public manipulation of region for test "bar"
region.testRegion<tagRegionTestBar>();
}
You can instantiate this for as many individual tests as you like. Of course testRegion
could also be a friend
or static
if you prefer. This can be misused, but only knowingly.