sarah
sarah

Reputation: 145

Accessing protected member functions from test code in C++

I've been racking my brain trying to think of the best way to access a protected member function from some test code in C++, here's my problem:

//in Foo.h 
Class Foo
{
protected:
    void DoSomething(Data data);
}

//in Blah.h
Class Blah
{
public:
    Foo foo;
    Data data; 
};

//in test code...
Blah blah;
blah.foo.DoSomething(blah.data); // Here's my problem!

Some possible solutions so far:

  • Make the test code class a friend of Foo, but this pollutes Foo with test code
  • Make DoSomething a public function
  • I've looked at creating a test wrapper for Foo, as suggested in this post, however this won't work as Blah contains the instance of Foo.

    All advice/insight/opinions are most welcome!

    Thanks

    Upvotes: 12

    Views: 10623

  • Answers (8)

    Dmitry
    Dmitry

    Reputation: 6770

    Rather than ifdefing private to public, consider ifdefing friendship, or better yet think if that function really needs to belong to that class, maybe it would suffice to have something in a named/unnamed namespace in a cpp, and then declared in a test project.

    Anyway, check this link, maybe your testing framework would provide similar functionality.

    EDIT: Did you consider inheriting your test class from your real class?

    Upvotes: 3

    Johannes Schaub - litb
    Johannes Schaub - litb

    Reputation: 506915

    There is a way which is completely allowed by the Standard.

    //in Foo.h 
    class Foo
    {
    protected:
        void DoSomething(Data data);
    };
    
    //in Blah.h
    class Blah
    {
    public:
        Foo foo;
        Data data; 
    };
    
    //in test code...
    struct FooExposer : Foo {
      using Foo::DoSomething;
    };
    
    Blah blah;
    (blah.foo.*&FooExposer::DoSomething)(blah.data);
    

    Read the Hidden features of C++ entry for an explanation.


    You may write a macro for your convenience (the parenthesis are there so that you can use this macro also for types that have a comma, like vector<pair<A, B>>):

    #define ACCESS(A, M, N) struct N : get_a1<void A>::type { using get_a1<void A>::type::M; }
    
    template<typename T> struct get_a1;
    template<typename R, typename A1> struct get_a1<R(A1)> { typedef A1 type; };
    

    The matter now becomes

    ACCESS((Foo), DoSomething, GetDoSomething);
    Blah blah;
    (blah.foo.*&GetDoSomething::DoSomething)(blah.data);
    

    Upvotes: 19

    Kirill V. Lyadvinsky
    Kirill V. Lyadvinsky

    Reputation: 99565

    Use wrapper as follows:

    // Foo.h unchanged
    
    // Blah.h unchanged
    
    // test code
    class FooTest : public Foo { friend void test(); }; // make friends
    
    void test()
    {
        Blah blah;
        static_cast<FooTest*>(&blah.foo)->DoSomething(blah.data); // Here's no problem!    
    }
    

    Upvotes: 2

    mocj
    mocj

    Reputation: 1486

    You could use inheritance with forwarding functions:

    class Foo
    {
    protected:
        void DoSomething(Data data);
    }
    
    class test_Foo : public Foo
    {
    public:
        void testDoSomething(Data data)
        {
            DoSomething(data);
        }
    }
    

    Upvotes: 2

    Murali VP
    Murali VP

    Reputation: 6417

    Ok, since you said it is only a test code I am going to suggest something seriously hacky but would work:

    struct tc : protected Foo
    {
        tc(Foo *foo, Data& data)
        {
            ((tc*)foo)->DoSomething(data);
        }
    };
    
    Blah blah;
    tc t(&blah.foo, blah.data);
    

    Upvotes: 8

    RichieHindle
    RichieHindle

    Reputation: 281425

    On the one hand, don't do that.

    On the other hand, here's a gamble:

    #define protected public
    #include "foo.h"
    #undef protected
    

    8-)

    But seriously, why is DoSomething() protected? Probably because calling it from external code can break something. In which case, you shouldn't be calling it from your tests.

    Upvotes: 7

    Dan Hook
    Dan Hook

    Reputation: 7077

    I've done

    class Foo
    {
    protected:
        void DoSomething(Data data);
    public:
        #ifdef TEST
        void testDoSomething(Data data);
        #endif
    }
    

    Then compile your unit tests with g++ -D TEST.

    Upvotes: 3

    lyricat
    lyricat

    Reputation: 1996

    If it is strictly test code, you could do...

    #define protected public
    #include "Foo.h"
    
    // test code
    
    #undef protected
    

    Upvotes: 1

    Related Questions