Capítulo 16: Testes Automatizados de Software com Catch2 e GoogleTest

Especialização em Sistemas com C++ • C++20 & C++23 • RAII, Smart Pointers, Templates, Concorrência e Alta Performance


🗺️ Mapa Conceitual da Pirâmide de Testes e Frameworks C++ Modernos

flowchart TD
    Testes["Engenharia de Qualidade em C++"] --> Unitarios["Testes Unitários (Micro-Componentes e Classes)"]
    Testes --> Integracao["Testes de Integração (Pipelines I/O e Concorrência)"]
    Testes --> Benchmarks["Microbenchmarks (Latência e Consumo de Cache)"]

    subgraph FRAMEWORKS["Frameworks Industriais"]
        Catch2["Catch2 (v3): BDD (SCENARIO/GIVEN/THEN) & Seções"]
        GTest["GoogleTest (GTest / GMock): Padrão Corporativo Amplo"]
    end

    subgraph CI_CD["Automação e Cobertura"]
        Sanitizers["Sanitizers (ASan, TSan, UBSan)"]
        Coverage["Gcov / Lcov / LLVM-Cov (Métricas de Cobertura)"]
    end

    Unitarios --> FRAMEWORKS
    Integracao --> FRAMEWORKS
    FRAMEWORKS --> CI_CD

    style Testes fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style FRAMEWORKS fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style CI_CD fill:#e8f5e9,stroke:#4caf50,stroke-width:2px

🏛️ 1. A Importância Crítica dos Testes em Sistemas sem Garbage Collector

Em linguagens de infraestrutura de sistemas como C++, onde a memória e os recursos do sistema operacional são controlados explicitamente pelo desenvolvedor, os testes automatizados são a primeira e mais crucial linha de defesa contra corrupção silenciosa de memória, condições de corrida (Race Conditions) e regressões de desempenho.

A prática moderna estabelece que:

  1. Nenhuma classe RAII ou algoritmo genérico deve entrar em produção sem testes automatizados que validem caminhos nominais e condições de contorno (edge cases).
  2. A suíte de testes deve rodar com Sanitizers ativados (AddressSanitizer e UndefinedBehaviorSanitizer).

⚙️ 2. Catch2 (v3): O Framework Idiomático Moderno

O Catch2 é amplamente reconhecido como o framework mais elegante e idiomático para C++ moderno. Diferente de frameworks antigos que exigiam macros legadas e classes extensas para cada teste, o Catch2 introduz seções aninhadas que compartilham código de inicialização de forma limpa:

#include <catch2/catch_test_macros.hpp>
#include <vector>

TEST_CASE("Vetores STL garantem capacidade e redimensionamento", "[vector]") {
    std::vector<int> v;

    // Seção 1
    SECTION("Inicialmente vazio") {
        REQUIRE(v.empty());
        REQUIRE(v.size() == 0);
    }

    // Seção 2: O Catch2 reinicializa o ambiente para cada SECTION!
    SECTION("Inserção de elementos preserva ordem") {
        v.push_back(10);
        v.push_back(20);

        REQUIRE(v.size() == 2);
        REQUIRE(v.front() == 10);
        REQUIRE(v.back() == 20);
    }
}

Principais Macros de Asserção do Catch2:

  • REQUIRE(expressão): Avalia a expressão; se falsa, interrompe imediatamente o caso de teste atual.
  • CHECK(expressão): Avalia a expressão; se falsa, reporta a falha, mas continua a execução dos passos subsequentes.
  • REQUIRE_THROWS_AS(expr, TipoExcecao): Valida se a expressão dispara a exceção esperada.

📦 3. Integração com Sanitizers (ASan, TSan e UBSan)

Escrever testes que apenas verificam o valor de retorno é insuficiente em C++. É imperativo compilar a suíte de testes com os instrumentadores de runtime do GCC e Clang:

# Compilação da suíte de testes com AddressSanitizer e UndefinedBehaviorSanitizer
g++ -std=c++20 -O1 -g -fsanitize=address,undefined tests.cpp -o run_tests
./run_tests

# Compilação para detecção de Race Conditions concorrentes
g++ -std=c++20 -O1 -g -fsanitize=thread tests.cpp -o run_thread_tests
./run_thread_tests

Com essas flags ativadas, qualquer buffer overflow, leitura de ponteiro desalocado (use-after-free) ou corrida de threads faz o executável de teste abortar imediatamente com um stack trace preciso contendo o arquivo e o número exato da linha da falha.


📊 4. Matriz Comparativa: Catch2 vs GoogleTest (GTest)

Característica Catch2 (v3) GoogleTest (GTest)
Estilo Sintático TEST_CASE e SECTION idiomáticos TEST e fixtures TEST_F tradicionais
Suporte a BDD Nativo (SCENARIO, GIVEN, WHEN, THEN) Suporte parcial via convenções
Decomposição de Expressões Automática (imprime valores de ambos os lados de ==) Requer macros específicas (EXPECT_EQ, EXPECT_NE)
Adoção Industrial Altíssima em projetos novos e open-source moderno Padrão dominante em corporações legadas e Android/Chromium