Test Class Private Members With GTest

14,673 views
Skip to first unread message

Dbger

unread,
Jun 28, 2010, 5:52:44 AM6/28/10
to googletes...@googlegroups.com
Hi, All

In GTest's official document, there is a link suggests how to test class's private member. However, I think FRIEND_TEST is way from convenient and come up with an new idea, it is simple, but works:

#ifdef GTEST
#define private public
#define protected public
#endif

By forcing all the class members to public in testing code we are able to access anything we want(details please see this blog entry, Chinese). And I thought it only change the access level of the members and should have no impact on the class's behavior. Do you see any potential risk in this approach?

Thanks.


技术博客:http://www.cnblogs.com/baiyanhuang/
我的豆瓣:http://www.douban.com/people/baiyanhuang/

Keith Ray

unread,
Jun 28, 2010, 2:34:13 PM6/28/10
to Dbger, googletes...@googlegroups.com
Here are a few reasons not to do that:

It becomes too easy for test code to delve into the private parts of
classes, and thus become fragile... when the class internals change,
the test code has to change as well.

If you refactor, moving code from test into production code, you or
your teammates may be surprised that the code no longer compiles in
production.

If the private parts of a class are interesting enough to test, they
probably belong to another class, where they are public, and tested
there. Search the web for the code smell description "Iceberg Class".

I've been in this situation: I spent half an hour trying to find out
why some test code was able to access private methods of a C++ class.
I had declared a method private so that I could find all the external
callers of that method [this is a "lean-on-the-compiler" step for
manually refactoring code -- make something illegal and then change
those pieces of code that no longer compile.] I eventuallty found
"#define private public" and removed it.

The more courteous thing would be define a macro like so:

#define TESTABLE_PRIVATE public

Courteous programmers don't redefine keywords of the language you are using.

--
C. Keith Ray, IXP Coach, Industrial Logic, Inc.
http://industriallogic.com 866-540-8336 (toll free)
Groove with our Agile Greatest Hits: http://www.industriallogic.com/elearning/
http://agilesolutionspace.blogspot.com/


2010/6/28 Dbger <baiya...@gmail.com>:

Novaisas

unread,
Jun 29, 2010, 5:43:24 AM6/29/10
to Keith Ray, Dbger, googletes...@googlegroups.com
I use the trick below:

TEST_F(SomeTest, Specific)
    {
    ClassWithPrivateMembers* pC;

    class TestClass : public ClassWithPrivateMembers
        {
        friend class SomeTest_Specific_Test;
        };

    TestClass* pTC = reinterpret_cast<TestClass*> (pC);

    // here you have access to the private members of pC by using pTC pointer
    }


Keith Ray

unread,
Jun 29, 2010, 1:19:54 PM6/29/10
to Novaisas, Dbger, googletes...@googlegroups.com
You can also use re-declare protected members of the parent class to
be public in the testable subclass. You don't have to repeat the
entire member declaration, just the name.

    class TestClass : public ClassWithPrivateMembers
        {
        public:
               memberYouWantPublic;
               // parent class has protected: memberYouWantPublic
        };

--
C. Keith Ray
 Web: http://industriallogic.com
 Twitter: @CKeithRay, @IndustrialLogic

Amplify Your Agility
Coaching | Training | Assessment | eLearning

Zhanyong Wan (λx.x x)

unread,
Jun 29, 2010, 3:23:09 PM6/29/10
to Keith Ray, Novaisas, Dbger, googletes...@googlegroups.com
On Tue, Jun 29, 2010 at 10:19 AM, Keith Ray <keit...@gmail.com> wrote:
> You can also use re-declare protected members of the parent class to
> be public in the testable subclass. You don't have to repeat the
> entire member declaration, just the name.
>
>     class TestClass : public ClassWithPrivateMembers
>         {
>         public:
>                memberYouWantPublic;

I think you mean

using memberYouWantPublic;

>                // parent class has protected: memberYouWantPublic
>         };
>
> --
> C. Keith Ray
>  Web: http://industriallogic.com
>  Twitter: @CKeithRay, @IndustrialLogic
>
> Amplify Your Agility
> Coaching | Training | Assessment | eLearning
> On Tue, Jun 29, 2010 at 2:43 AM, Novaisas <nova...@gmail.com> wrote:
>>
>> I use the trick below:
>>
>> TEST_F(SomeTest, Specific)
>>     {
>>     ClassWithPrivateMembers* pC;
>>
>>     class TestClass : public ClassWithPrivateMembers
>>         {
>>         friend class SomeTest_Specific_Test;
>>         };
>>
>>     TestClass* pTC = reinterpret_cast<TestClass*> (pC);
>>
>>     // here you have access to the private members of pC by using pTC pointer
>>     }
>>
>>
>

--
Zhanyong

Zhanyong Wan (λx.x x)

unread,
Jun 29, 2010, 3:35:42 PM6/29/10
to Novaisas, Keith Ray, Dbger, googletes...@googlegroups.com
On Tue, Jun 29, 2010 at 2:43 AM, Novaisas <nova...@gmail.com> wrote:
> I use the trick below:
>
> TEST_F(SomeTest, Specific)
>     {
>     ClassWithPrivateMembers* pC;
>
>     class TestClass : public ClassWithPrivateMembers
>         {
>         friend class SomeTest_Specific_Test;

Uh, please don't refer to the name SomeTest_Specific_Test directly.
That's gtest's implementation detail and may change at any time
without notice, which is why we have FRIEND_TEST.

>         };
>
>     TestClass* pTC = reinterpret_cast<TestClass*> (pC);

I think you mean static_cast here. reinterpret_cast is for a different purpose.

>
>     // here you have access to the private members of pC by using pTC
> pointer

I think you'll have access to the protected members, but not the private ones.

>     }
>
>
>

--
Zhanyong

Keith Ray

unread,
Jun 29, 2010, 4:34:23 PM6/29/10
to Zhanyong Wan (λx.x x), Novaisas, Dbger, googletes...@googlegroups.com
yes - I meant "using"

2010/6/29 Zhanyong Wan (λx.x x) <w...@google.com>:

--

Zhanyong Wan (λx.x x)

unread,
Jun 29, 2010, 5:11:17 PM6/29/10
to Dbger, googletes...@googlegroups.com
Hi,

On Mon, Jun 28, 2010 at 2:52 AM, Dbger <baiya...@gmail.com> wrote:
> Hi, All
> In GTest's official document, there is a link suggests how to test class's
> private member. However, I think FRIEND_TEST is way from convenient and come
> up with an new idea, it is simple, but works:
>>
>> #ifdef GTEST
>>
>> #define private public

This is invalid C++. According to C++98 standard:

17.4.3.11 Macro names [lib.macro.names]

"Nor shall ... define macros for names lexically identical to keywords."

In other words, you are not allowed to #define a C++ keyword. While
your current compiler may not mind, the code is not portable, and may
break with the next version of the compiler.

>> #define protected public
>>
>> #endif
>
> By forcing all the class members to public in testing code we are able to
> access anything we want(details please see this blog entry, Chinese). And I
> thought it only change the access level of the members and should have no
> impact on the class's behavior. Do you see any potential risk in this
> approach?

In addition to the #define isn't allowed, changing the access
specifiers can affect the lay-out of the non-static member variables:

9.2 Class members, paragraph 12:

"The order of allocation of nonstatic data members separated by an
access-specifier is unspecified."

In other words, the compiler is free to re-arrange the order of
different access specifier groups. For example, a compiler is allowed
to put all private data members before other data members, and your
trick will change the object lay-out, causing subtle problems when
your test code is linked with production code.

--
Zhanyong

Zhanyong Wan (λx.x x)

unread,
Jun 29, 2010, 5:12:10 PM6/29/10
to Keith Ray, Dbger, googletes...@googlegroups.com
On Mon, Jun 28, 2010 at 11:34 AM, Keith Ray <keit...@gmail.com> wrote:
> Here are a few reasons not to do that:
>
> It becomes too easy for test code to delve into the private parts of
> classes, and thus become fragile... when the class internals change,
> the test code has to change as well.
>
> If you refactor, moving code from test into production code, you or
> your teammates may be surprised that the code no longer compiles in
> production.
>
> If the private parts of a class are interesting enough to test, they
> probably belong to another class, where they are public, and tested
> there. Search the web for the code smell description "Iceberg Class".
>
> I've been in this situation: I spent half an hour trying to find out
> why some test code was able to access private methods of a C++ class.
> I had declared a method private so that I could find all the external
> callers of that method [this is a "lean-on-the-compiler" step for
> manually refactoring code -- make something illegal and then change
> those pieces of code that no longer compile.] I eventuallty found
> "#define private public" and removed it.
>
> The more courteous thing would be define a macro like so:
>
> #define TESTABLE_PRIVATE  public

Don't even do this, as it may change the object lay-out.

>
> Courteous programmers don't redefine keywords of the language you are using.
>
> --
> C. Keith Ray, IXP Coach, Industrial Logic, Inc.
> http://industriallogic.com      866-540-8336 (toll free)
> Groove with our Agile Greatest Hits: http://www.industriallogic.com/elearning/
> http://agilesolutionspace.blogspot.com/
>
>
> 2010/6/28 Dbger <baiya...@gmail.com>:
>> Hi, All
>> In GTest's official document, there is a link suggests how to test class's
>> private member. However, I think FRIEND_TEST is way from convenient and come
>> up with an new idea, it is simple, but works:
>>>
>>> #ifdef GTEST
>>>
>>> #define private public
>>>
>>> #define protected public
>>>
>>> #endif
>>
>> By forcing all the class members to public in testing code we are able to
>> access anything we want(details please see this blog entry, Chinese). And I
>> thought it only change the access level of the members and should have no
>> impact on the class's behavior. Do you see any potential risk in this
>> approach?
>> Thanks.
>>
>> 技术博客:http://www.cnblogs.com/baiyanhuang/
>> 我的豆瓣:http://www.douban.com/people/baiyanhuang/
>>
>

--
Zhanyong

Novaisas

unread,
Jun 29, 2010, 5:35:45 PM6/29/10
to Zhanyong Wan (λx.x x), Keith Ray, Dbger, googletes...@googlegroups.com
2010/6/29 Zhanyong Wan (λx.x x) <w...@google.com>
Uh, please don't refer to the name SomeTest_Specific_Test directly.
That's gtest's implementation detail and may change at any time
without notice, which is why we have FRIEND_TEST.

Thanks for the tip. This helped me to find http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide#Testing_Private_Code I have to read the guide after all :)

I think you mean static_cast here.  reinterpret_cast is for a different purpose.

Oh yes, a little too strong for the case.
 
I think you'll have access to the protected members, but not the private ones.


You are correct again. It happened so I have tried it only on protected members, but thought it will work on private too. Now I see why.
Reply all
Reply to author
Forward
0 new messages