Me deparei com um problema comum ao escrever testes unitários: precisava testar métodos privados de uma classe sem modificar o código de produção. A solução envolve herança e a palavra-chave using.
O Problema
Considere uma classe que calcula cargas estruturais em uma edificação:
class Calculator {
private:
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
public:
int calculate(int x, int y) {
int sum = add(x, y);
return multiply(sum, 2);
}
};
Óbviamente não é código de produção.
Testar apenas getTotalDesignLoad seria testar indiretamente os cálculos intermediários, dificultando minha vida.
A Solução: Classe Derivada com Using
Criei uma fixture de teste que herda da classe original e expõe os métodos privados:
class TestCalculator : public Calculator {
public:
using Calculator::add;
using Calculator::multiply;
};
TEST(CalculatorTest, AddWorks) {
TestCalculator calc;
EXPECT_EQ(5, calc.add(2, 3));
EXPECT_EQ(0, calc.add(-1, 1));
}
TEST(CalculatorTest, MultiplyWorks) {
TestCalculator calc;
EXPECT_EQ(6, calc.multiply(2, 3));
EXPECT_EQ(0, calc.multiply(5, 0));
}
Como Funciona
A declaração using em uma classe derivada altera o nível de acesso dos membros herdados. Métodos que são private na classe base podem ser tornados public na classe derivada.
Isso funciona porque, a classe derivada herda todos os membros da classe base, incluindo os privados.
A declaração using não copia o método, apenas ajusta sua visibilidade. Com isso o código de produção permanece inalterado.
Alternativa: Friend Class
Outra abordagem é declarar a fixture como friend:
class Calculator {
friend class TestCalculator;
private:
int add(int a, int b) { return a + b; }
};
Essa solução tem uma desvantagem: requer modificar o código de produção para adicionar a declaração friend, acoplando a implementação aos testes.
E fica feio.
Quando Usar Cada Abordagem
Use a classe derivada com using quando não pode modificar a classe original, quer manter o código de testes completamente separado.
Use friend quando a classe tem membros privados que não podem ser acessados via herança e/ou precisa acessar atributos privados diretamente, não apenas métodos