Самый чистый способ с точки зрения инкапсуляции:
// В заголовочном файле класса
class MyClass {
private:
int internalCalculation(int x) { /* ... */ }
// Объявляем тестовый класс другом
friend class MyClassTest;
};
// В файле тестов
class MyClassTest {
public:
static int testInternalCalculation(MyClass& obj, int x) {
return obj.internalCalculation(x);
}
};
TEST(MyClassTest, InternalCalculation) {
MyClass obj;
ASSERT_EQ(42, MyClassTest::testInternalCalculation(obj, 42));
}
class TestableClass : public MyClass {
public:
// Делаем protected методы публичными для тестов
using MyClass::internalMethod;
};
Рефакторинг для лучшей тестируемости:
// Было:
class MyClass {
private:
int complexLogic(int a, int b) { /* ... */ }
};
// Стало:
class ComplexLogic {
public:
static int calculate(int a, int b) { /* ... */ }
};
class MyClass {
private:
ComplexLogic logic; // Теперь можно тестировать ComplexLogic отдельно
};
Не рекомендуется, но иногда используется:
class MyClass {
private:
int internalMethod() {
#ifdef UNIT_TESTING
return testableInternalMethod();
#else
return realInternalMethod();
#endif
}
};
template<typename T>
auto accessPrivate(T& obj, int T::* ptr) {
return obj.*ptr;
}
TEST(MyClassTest, AccessPrivateMember) {
MyClass obj;
auto value = accessPrivate(obj, &MyClass::privateMember);
ASSERT_EQ(42, value);
}
class MyClass {
private:
void hiddenMethod() {}
};
TEST(MyClassTest, CallHiddenMethod) {
MyClass obj;
auto method = &MyClass::hiddenMethod;
(obj.*method)(); // Вызов приватного метода
}
Основные способы тестирования закрытых методов:
Главный принцип: если приватный метод сложен настолько, что требует отдельного тестирования - возможно, он должен быть:
Помните, что чрезмерное тестирование приватных методов может привести к хрупким тестам, которые ломаются при любом изменении реализации.