Pruebas en C++: revisión de frameworks y herramientas
Las pruebas son una parte esencial del desarrollo profesional en C++. A diferencia de los lenguajes con tipado dinámico, C++ requiere especial atención a la gestión de memoria, RAII y metaprogramación con plantillas. Sin pruebas de calidad, incluso una pequeña refactorización puede provocar errores difíciles de detectar. En este artículo, analizaremos los principales frameworks para pruebas unitarias en C++, sus características y ejemplos de uso.
¿Por qué probar el código C++?
Razones principales para implementar pruebas:
- Seguridad en la refactorización — las pruebas fijan el comportamiento correcto antes de los cambios.
- Documentación — las pruebas muestran cómo usar la API.
- Detección de regresiones — verificación automática después de cada commit.
- Validación de casos límite — trabajo con nullptr, contenedores vacíos, desbordamiento.
Frameworks populares para C++
1. Google Test (gtest)
El framework más común de Google. Soporta registro automático de pruebas, macros ASSERT_* y EXPECT_*, pruebas parametrizadas, mocks a través de Google Mock.
#include <gtest/gtest.h>
int add(int a, int b) { return a + b; }
TEST(AdditionTest, PositiveNumbers) { EXPECT_EQ(add(2, 3), 5); EXPECT_EQ(add(0, 0), 0);}
TEST(AdditionTest, NegativeNumbers) { EXPECT_EQ(add(-1, -2), -3);}
int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();}Características:
- Soporte para death tests (pruebas de muerte) para verificar assert.
- Fábrica de mocks (gmock) para aislar dependencias.
- Integración con CMake (find_package(GTest)).
2. Catch2
Framework moderno con sintaxis expresiva. No requiere registro separado de pruebas — basta con la macro TEST_CASE. Soporta estilo BDD (Given-When-Then).
#include <catch2/catch_test_macros.hpp>
int factorial(int n) { return n <= 1 ? 1 : n * factorial(n - 1);}
TEST_CASE("Factorials are computed", "[math]") { REQUIRE(factorial(0) == 1); REQUIRE(factorial(1) == 1); REQUIRE(factorial(5) == 120);}
TEST_CASE("Negative input", "[math][edge]") { REQUIRE_THROWS_AS(factorial(-1), std::domain_error);}Características:
- Generación de datos de prueba (GENERATE).
- División de pruebas en secciones (SECTION).
- Selección automática de main() (si se define CATCH_CONFIG_MAIN).
3. doctest
Framework ligero y rápido, tiempo de compilación mínimo. Ideal para proyectos donde la velocidad de compilación es importante. La sintaxis es similar a Catch2.
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN#include <doctest/doctest.h>
int divide(int a, int b) { if (b == 0) throw std::invalid_argument("division by zero"); return a / b;}
TEST_CASE("Division") { CHECK(divide(10, 2) == 5); CHECK_THROWS_AS(divide(5, 0), std::invalid_argument);}Características:
- Compilación muy rápida (archivo de cabecera ~5 KB).
- Soporte para macros CHECK, REQUIRE, WARN.
- Reporte integrado en XML/JSON.
4. Boost.Test
Parte de las bibliotecas Boost. Tiene capacidades ricas, pero requiere más código boilerplate. Soporta registro automático y manual de pruebas.
#define BOOST_TEST_MODULE MyTest#include <boost/test/included/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(MathTests)
BOOST_AUTO_TEST_CASE(TestSum) { BOOST_CHECK_EQUAL(2 + 2, 4); BOOST_CHECK_CLOSE(3.14159, 3.14, 0.1); // precisión 0.1%}
BOOST_AUTO_TEST_CASE(TestFail) { BOOST_REQUIRE_MESSAGE(false, "This test always fails");}
BOOST_AUTO_TEST_SU También en la biblioteca
Trabajo con imágenes en Kotlin: bibliotecas y herramientas
Registro en Go (Golang): mejores bibliotecas y soluciones para producción
Trabajo con Apache Kafka en C++: librería librdkafka y ejemplos prácticos
Procesamiento de JSON en Kotlin: bibliotecas y ejemplos de uso