How to Test Private Members of a Class Using GoogleTest
Use the FRIEND_TEST macro defined in googletest/include/gtest/gtest_prod.h to declare your test as a friend of the class, granting compile-time access to private methods and data members without exposing them in the public API.
When writing unit tests for C++ classes, you often need to verify internal state or helper methods that are intentionally encapsulated. The GoogleTest framework provides a lightweight, zero-overhead mechanism to test private members of a class using the FRIEND_TEST macro, allowing you to maintain strict encapsulation in production code while achieving thorough test coverage.
Understanding the FRIEND_TEST Mechanism
The FRIEND_TEST macro is declared in googletest/include/gtest/gtest_prod.h. When placed inside a class definition, it expands to a friend class declaration that matches the internal name generated by Google Test for your test case.
Google Test internally creates a class named test_case_name_test_name_Test. The FRIEND_TEST(test_case_name, test_name) macro makes that specific generated class a friend of your production class. Because friendship bypasses C++ access control, the test can directly call private methods, read private data members, or invoke private constructors without requiring public accessors.
This approach resolves at compile time, meaning there is zero runtime overhead and no impact on the production API surface.
How to Test Private Members of a Class Using GoogleTest
Testing Private Methods with TEST
Place the FRIEND_TEST macro inside your class definition, typically in the private: section near the members you want to expose:
// my_class.h
#include <gtest/gtest_prod.h>
class MyClass {
public:
MyClass(int v) : value_(v) {}
private:
int ComputeSecret() const { return value_ * 42; }
// Grant the test `MyClassTest.CanAccessPrivate` access to private members.
FRIEND_TEST(MyClassTest, CanAccessPrivate);
};
In your test file, use the standard TEST macro with the exact same test case and test names specified in the FRIEND_TEST declaration:
// my_class_test.cpp
#include "my_class.h"
#include <gtest/gtest.h>
TEST(MyClassTest, CanAccessPrivate) {
MyClass obj(7);
// Directly call the private method because the test is a declared friend.
EXPECT_EQ(obj.ComputeSecret(), 294);
}
Using FRIEND_TEST with Test Fixtures (TEST_F)
The macro works identically with fixture-based tests. Declare the fixture class name as the test case name:
// my_class.h
class MyClass {
public:
explicit MyClass(int v) : value_(v) {}
private:
int value_;
int Multiply(int factor) const { return value_ * factor; }
// Declare the fixture class as a friend.
FRIEND_TEST(MyClassFixtureTest, UsesPrivateMethod);
};
// my_class_fixture_test.cpp
#include "my_class.h"
#include <gtest/gtest.h>
class MyClassFixtureTest : public ::testing::Test {
protected:
MyClass obj_{5};
};
TEST_F(MyClassFixtureTest, UsesPrivateMethod) {
// Because the fixture is a friend, we can call the private method.
EXPECT_EQ(obj_.Multiply(3), 15);
}
Accessing Private Constructors
FRIEND_TEST also enables instantiation of objects that enforce construction restrictions:
// secret.h
#include <gtest/gtest_prod.h>
class Secret {
private:
Secret(int v) : value_(v) {}
int value_;
FRIEND_TEST(SecretTest, PrivateCtor);
};
// secret_test.cpp
#include "secret.h"
#include <gtest/gtest.h>
TEST(SecretTest, PrivateCtor) {
// Directly invoke the private constructor.
Secret s(42);
// No public API needed.
}
Alternative Approaches When Modifying Production Headers Is Not Possible
If you cannot add FRIEND_TEST to the production class, consider these patterns:
- Test-only subclass: Expose needed functionality via protected accessors in a subclass used only for testing.
- Fixture inheritance: Inherit from the class in your test fixture and test protected members through the fixture interface.
- Conditional compilation: Wrap private member accessors in thin functions guarded by
#ifdef TEST_BUILDpreprocessor directives.
Summary
- The
FRIEND_TESTmacro ingoogletest/include/gtest/gtest_prod.hdeclares specified tests as friends of your class. - Place the macro inside the class definition using the exact test case and test names from your
TESTorTEST_Fdeclarations. - The mechanism works for private methods, data members, and constructors without exposing them in the public API.
- Friendship is resolved at compile time, resulting in zero runtime overhead or binary size impact.
- When header modification is impossible, use test-only subclasses or conditional compilation to access internal state.
Frequently Asked Questions
Can I use FRIEND_TEST with parameterized tests (TEST_P)?
Yes. The FRIEND_TEST macro works with TEST_P and TEST_F because it grants friendship to the underlying test class generated by GoogleTest. Use the test case name (the first argument to TEST_P) as the first argument to FRIEND_TEST.
Does FRIEND_TEST expose private members in production builds?
No. The macro expands to a standard C++ friend class declaration, which is strictly a compile-time construct. Private members remain inaccessible to client code, and the friendship declaration does not affect runtime performance or binary size in release builds.
Where should I place the FRIEND_TEST macro in my class definition?
Place the macro in the same access control block as the private members you intend to test, typically in the private: or protected: section immediately after the relevant members. While the C++ standard allows friend declarations anywhere in the class body, keeping the macro near the private members improves code readability and maintainability.
What if I cannot modify the class source code to add FRIEND_TEST?
If modifying the production header is not possible, create a test-only subclass that exposes the necessary members through protected accessors, or use a test fixture that inherits from the class and accesses protected members. You can also wrap private functionality in thin accessor functions guarded by preprocessor directives like #ifdef TEST_BUILD.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →