1

I am trying to figure out how FRIEND_TEST works in Google Tests. https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#testing-private-code

I am looking at the following item, trying to implement it in my code:

// foo.h
#include "gtest/gtest_prod.h"

// Defines FRIEND_TEST.
class Foo {
  ...
 private:
  FRIEND_TEST(FooTest, BarReturnsZeroOnNull);
  int Bar(void* x);
};

// foo_test.cc
...
TEST(FooTest, BarReturnsZeroOnNull) {
  Foo foo;
  EXPECT_EQ(0, foo.Bar(NULL));
  // Uses Foo's private member Bar().
}

In the code above, the piece that I can't see, is that foo_test.cc must include foo.h, in order to have access to Foo and Bar(). [Perhaps it works differently for Google ? in my code, I must include it]

That will result in circular dependency...

Am I missing something ?

Edit: code sample: (re-edited after fixing - solution being changing test file from *.h to *.cpp):

Project ppppp - file myfile.h:

class INeedToBeTested
{
public:
  extern friend class blah_WantToTestThis_Test;
  INeedToBeTested();
  INeedToBeTested(INeedToBeTested* item);
  INeedToBeTested(OtherClass* thing, const char* filename);
  ~INeedToBeTested();
  bool Testable();
  std::string MoreTestable();

private:
  int WantToTestThis();
};

Project ppppp_gtest, file myFile_gtest.cpp:

#pragma once
#include "gtest/gtest.h"
#include "myfile.h" //property sheet adds include locations
#include "otherclass.h"

  class blah: public ::testing::Test{
  // declarations, SetUp, TearDown to initialize otherclass thing, std::string filename
  }
  TEST_F(blah, WantToTestThis)
      {
        INeedToBeTested item(thing, filename.c_str());
        item.WantToTestThis();   // inaccessible when this content is in header file
      }

During my efforts to get this to work, I also experimented with creating a wrapper class (this also works only if in a cpp, not in header); while it requires changing private to protected, it doesn't require additional declarations inside tested code which each new test:

// option: create wrapper (change private to protected first) 
  class INeedToBeTestedWrapper:public INeedToBeTested 
      {
      public:
         INeedToBeTestedWrapper(OtherClass* thing, std::string filename):
            INeedToBeTested(OtherClass* thing, filename.c_str());
      public:
         using INeedToBeTested::WantToTestThis;
      };

      TEST_F(blah, WantToTestThis)
      {
        INeedToBeTestedWrapper item(thing, filename);
        item.WantToTestThis();   
      }
rold2007
  • 1,297
  • 1
  • 12
  • 25
Thalia
  • 13,637
  • 22
  • 96
  • 190

1 Answers1

4

There shouldn't be a problem here.

FRIEND_TEST in this case simply defines

friend class FooTest_BarReturnsZeroOnNull_Test;

which is the class ultimately defined by using the TEST macro. There's no need to link gtest or any of your test code to the foo library/exe. You only need to #include "gtest/gtest_prod.h" as you have done.

In foo_test.cc, you need to #include "foo.h" since it's using an actual instance of a Foo object. You also need to link your foo library to the test executable, or if foo isn't a library, you need to compile in the foo sources.

So in summary, foo doesn't need any test code with the exception of the tiny gtest_prod.h header, but the test needs linked to the foo code.

Fraser
  • 74,704
  • 20
  • 238
  • 215
  • 1
    I was unable to make it work - I tried not including gtest/gtest_prod.h, but write instead the actual "friend class..." (it also seems more clear, and it gave me a better understanding of how gtest actually works). The problem may be a namespace issue - the fact that the production code is in an anonymous namespace, in a separate project - and if I actually include the test namespace (see http://www.codeproject.com/Articles/484817/Google-e2-80-99splusgtestplusnotplushandlingplusfr), I get an error, about the test namespace being invalid. – Thalia Nov 01 '12 at 17:39
  • 1
    @Mihaela Do you mean the class declaration in foo.h is in an anonymous namespace? If you add examples of your namespace layouts to the question, it would probably help a wee bit. As the codeproject article points out, the `FRIEND_TEST` macro doesn't handle namespaces well, but if you're happy to abandon the macro and just declare the test class a friend normally, then the namespace issues should be easier to handle. – Fraser Nov 01 '12 at 18:57
  • I have added code... with 2 implementation versions. One that adds the friend class, another where I even abandoned that concept, changed visibility of 'private' to 'protected' and tried to create a wrapper class, as the FAQ in the Google Test documentation suggests. (of course, if I add the friend with 'extern' I remove the "namespace - but I got link errors) – Thalia Nov 01 '12 at 20:42
  • @Mihaela Yes - you need to move the test fixture (the `TEST_F(blah, WantToTestThis)` bit) outside of the anonymous namespace. It doesn't exist outside of the scope of that compilation unit, so it can't access `INeedToBeTested`'s private members since `INeedToBeTested` can't "see" it to make it a friend. The test class can stay in the anonymous namespace if you wish. Then if you add `friend class blah_WantToTestThis_Test;` to `INeedToBeTested`, you're good to go. – Fraser Nov 01 '12 at 22:36
  • And then I get link errors: private virtual void __thiscall blah_WantToTestThis_Test::TestBody(void).. already defined and private static class testing::TestInfo * blah_WantToTestThis_Test::test_info_.. already defined – Thalia Nov 01 '12 at 22:45
  • Just noticed that all your test code is in a header! The declaration of your test class can stay in the header, but the definitions and test fixtures should normally be in a corresponding cpp file. You probably also want to [avoid using anonymous namespaces in headers](http://stackoverflow.com/q/357564/424459) and make sure you have [header guards](http://stackoverflow.com/q/2979384/424459) in your header files. – Fraser Nov 01 '12 at 22:54
  • Updated code in post: placed macro in cpp. No longer builds, "member function redeclaration not allowed", also "cannot access private member". Added ifnded, define, endif too - did not help. – Thalia Nov 01 '12 at 23:16
  • 1
    @Mihaela Nearly there :-) Delete the `TEST_F` declaration from the header myFile_gtest.h Google's `TEST_F` macro is actually declaring the class `blah_WantToTestThis_Test`, so even though you don't implement any of the `TEST_F` code in the header, you're declaring the class twice. (You could probably delete the whole header file actually, unless some other cpp file(s) need access to your class `blah`.) Then move the `TEST_F` body outside of the anonymous namespace in myFile_gtest.cpp so it can be befriended by `INeedToBeTested`. – Fraser Nov 01 '12 at 23:25
  • So, basically I would have been fine from the beginning have I created a cpp test file instead of a .h test file ? That is really good to know, I always believed that .h would be better - and keeping functions in it ("inlined" if they are small), faster, and so on... Thank you so much for all your help, and your patience ! – Thalia Nov 01 '12 at 23:34
  • @Mihaela No worries. Glad we got it in the end. – Fraser Nov 01 '12 at 23:36