C++: Testes Unitários
Última atualização: 2026-08-26
Na aula 49, aprendemos sobre otimização de desempenho.
Agora, vamos aprender sobre testes unitários — a habilidade-chave para garantir a qualidade do código.
Escrever código sem testar é como cozinhar sem provar.
1. Visão Geral de Testes Unitários
(1) 1.1 O Que São Testes Unitários?
Testes unitários envolvem testar as menores unidades testáveis (funções, classes).
Vantagens:
- Detectar bugs cedo
- Rede de segurança para refatoração
- Documenta o comportamento do código
(2) 1.2 Escolhendo um Framework de Teste
| Framework | Descrição |
|---|---|
| Google Test | Feito pelo Google, mais popular |
| Catch2 | Arquivo header único, simples de usar |
| Boost.Test | Parte das bibliotecas Boost |
| Doctest | Similar ao Catch2, mais rápido |
Recomendação: Google Test (completo) ou Catch2 (simples)
2. Introdução ao Google Test
(1) 2.1 Instalando o Google Test
Linux:
sudo apt-get install libgtest-dev
Windows: Use vcpkg ou compile do código-fonte.
(2) 2.2 Seu Primeiro Teste
Exemplo: Testando uma função de adição com Google Test (Dificuldade ⭐)
▶ Exemplo 2: Exemplo de código (Dificuldade ⭐)
#include <gtest/gtest.h>
int add(int a, int b) {
return a + b;
}
TEST(AddTest, Positive) {
EXPECT_EQ(add(2, 3), 5);
}
TEST(AddTest, Negative) {
EXPECT_EQ(add(-2, -3), -5);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Saída:
[==========] Running 2 tests from 1 test suite.
[ PASSED ] 2 tests.
Resultado da execução:
[==========] Running 2 tests from 1 test suite.
[ PASSED ] 2 tests.
3. Macros de Asserção
(1) 3.1 ASSERT vs EXPECT
| Macro | Comportamento em Caso de Falha |
|---|---|
ASSERT_* |
Para o teste atual |
EXPECT_* |
Continua executando |
Recomendação: Use EXPECT_* (você pode ver mais falhas)
(2) 3.2 Asserções Comuns
| Asserção | Descrição |
|---|---|
EXPECT_EQ(a, b) |
a == b |
EXPECT_NE(a, b) |
a != b |
EXPECT_LT(a, b) |
a < b |
EXPECT_GT(a, b) |
a > b |
EXPECT_TRUE(cond) |
cond é verdadeiro |
EXPECT_FALSE(cond) |
cond é falso |
EXPECT_THROW(statement, exception_type) |
Espera que uma exceção seja lançada |
4. Fixtures de Teste
(1) 4.1 Por Que Precisamos de Fixtures?
Problema: Múltiplos testes precisam do mesmo código de inicialização.
Solução: Fixtures de teste (herdar de testing::Test)
(2) 4.2 Exemplo: Testando uma fila com fixtures (Dificuldade ⭐⭐)
#include <gtest/gtest.h>
#include <queue>
class QueueTest : public testing::Test {
protected:
void SetUp() override {
q.push(1);
q.push(2);
q.push(3);
}
void TearDown() override {
// Limpeza (se necessário)
}
std::queue<int> q;
};
TEST_F(QueueTest, Size) {
EXPECT_EQ(q.size(), 3);
}
TEST_F(QueueTest, Front) {
EXPECT_EQ(q.front(), 1);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Saída:
(Saída do programa)
5. Desenvolvimento Orientado a Testes (TDD)
(1) 5.1 O Que É TDD?
TDD (Test-Driven Development) significa escrever testes primeiro, depois escrever o código.
Processo:
- Escreva um teste (ele falha)
- Escreva o código (ele passa)
- Refatore
(2) 5.2 Exemplo: TDD para uma Pilha (Dificuldade ⭐⭐⭐)
// 1. Escreva o teste primeiro
TEST(StackTest, PushAndPop) {
Stackint s;
s.push(42);
EXPECT_EQ(s.pop(), 42);
}
// 2. Depois escreva o código
template<typename T>
class Stack {
std::vectorT data;
public:
void push(const T& x) { data.push_back(x); }
T pop() { T x = data.back(); data.pop_back(); return x; }
};
Saída:
(Saída do programa)
6. Prática: Testando uma Classe Student
▶ Exemplo 1: Teste completo (Dificuldade ⭐⭐⭐)
#include <gtest/gtest.h>
#include <string>
class Student {
std::string name;
int age;
public:
Student(const std::string& name, int age) : name(name), age(age) {}
std::string getName() const { return name; }
int getAge() const { return age; }
};
TEST(StudentTest, Constructor) {
Student s("Zhang San", 20);
EXPECT_EQ(s.getName(), "Zhang San");
EXPECT_EQ(s.getAge(), 20);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Saída:
(Saída do programa)
▶ Exemplo 3: Teste unitário simples (Dificuldade ⭐)
#include <iostream>
#include <cassert>
// Funções sendo testadas
int multiply(int a, int b) {
return a * b;
}
bool isPositive(int n) {
return n > 0;
}
int main() {
// Testar multiply
assert(multiply(2, 3) == 6);
assert(multiply(-2, 3) == -6);
assert(multiply(0, 5) == 0);
std::cout << "multiply Test passed" << std::endl;
// Testar isPositive
assert(isPositive(5) == true);
assert(isPositive(-1) == false);
assert(isPositive(0) == false);
std::cout << "isPositive Test passed" << std::endl;
std::cout << "All tests passed!" << std::endl;
return 0;
}
Saída:
multiply Test passed
isPositive Test passed
All tests passed!
❓ Perguntas Frequentes
P: Testes unitários podem encontrar todos os bugs? R: Não. Testes unitários só podem encontrar tipos conhecidos de erros. Você também precisa de testes de integração e testes de sistema.
P: O código de teste deve ser enviado junto com o código de produção? R: Não. O código de teste é tipicamente usado apenas durante o desenvolvimento.
P: Como testo funções privadas? R: - Método 1: Teste a interface pública (teste indireto) - Método 2: Use FRIEND_TEST para expor membros privados - Método 3: Refatore extraindo funções privadas em uma classe separada
📖 Resumo
| Ponto-Chave | Resumo |
|---|---|
| Teste unitário | Testar as menores unidades |
| Google Test | Framework de teste C++ mais popular |
| Macros de asserção | EXPECT_* e ASSERT_* |
| Fixtures de teste | Compartilhar código de inicialização |
| TDD | Escrever testes primeiro, depois código |
📝 Exercícios
-
Básico (Dificuldade ⭐): Escreva uma função
add(a, b)e crie 3 casos de teste com Google Test: adição de positivos, adição de negativos, adição de zeros. -
Intermediário (Dificuldade ⭐⭐): Escreva uma função
isPalindromeque verifica se uma string é um palíndromo. Escreva pelo menos 5 casos de teste cobrindo entradas normais, de borda e especiais. -
Desafio (Dificuldade ⭐⭐⭐): Escreva testes unitários completos para uma classe "Conta Bancária" (com métodos depositar/sacar/getBalance). Teste depósitos/saques normais, rejeição de saque acima do saldo e segurança de thread (se aplicável).
- Testes unitários verificam a corretude de funções ou módulos
- Frameworks de teste: Google Test / Catch2 / doctest
- Estrutura do caso de teste: Arrange → Act → Assert
- Desenvolvimento orientado a testes (TDD): escrever testes primeiro, depois código
- Cobertura de código mede quão completos são seus testes
Próxima aula: Prática — Sistema de Gerenciamento de Contatos (#51)