CppUnit
La librería que estoy usando usa CppUnit >>, por lo tanto, la tengo como shared library en /usr/include.
Sin embargo, para usar la librería hay que enlazarla en el linker con -lcppunit.
TestCase
Lo más básico es que hay que extender CppUnit::TestCase y sobrescribir la función runTest >>
#include<cppunit/TestCase.h> class MiTest : public CppUnit::TestCase{ void runTest(){ CPPUNIT_ASSERT(true); } };
Ya pero, para correrlo, necesariamente ¿tengo que meterlo dentro de un Fixture, al cual llamará el TestCaller? ¿metiéndolo en una Suite y corriéndola con un TestRunner? ¿Es sólo ésto lo que puedo llamar desde un método main? Sin mencionar macros ni factories, etc. Es decir ¿cuál es la implementación más simple? Yo después veré si necesito lo demás… >>
No he logrado encontrar cómo correr el código anterior, así es que vamos con el tutorial completo…
Fixture
Luego, al parecer efectivamente debo tener un Fixture (que sirve para inicializar y destruír los objetos de prueba, y se usan en los varios TestCase que componen el Fixture)
#include<cppunit/TestCase.h> //nótese #include<cppunit/TestCaller.h> #include<cppunit/TestResult.h> class MiTestFixture : public CppUnit::TestFixture{ public: void metodoTest(){ CPPUNIT_ASSERT(false); } }; int main( int argc, char **argv){ CppUnit::TestCaller<MiTestFixture> test ("metodoTest",&MiTestFixture::metodoTest); CppUnit::TestResult resultado; test.run(&resultado); }
Ésto corre… al parecer… pero no da ningún output. Así es que, de hecho, necesito un TestRunner.
TestSuite
Para éso, necesito hacer primero una TestSuite.
#include<cppunit/TestCase.h> #include<cppunit/TestCaller.h> #include<cppunit/TestResult.h> //nótese #include<cppunit/TestSuite.h> //MiTestFixture idem... int main( int argc, char **argv){ CppUnit::TestSuite suite; CppUnit::TestResult resultado; suite.addTest(new CppUnit::TestCaller<MiTestFixture> ("metodoTest",&MiTestFixture::metodoTest)); suite.run(&resultado); }
Lo cual todavía no da output.
TestRunner
Entonces le metemos el TestRunner, para el ejemplo lo voy a hacer todo en el main, aunque en el tutorial ponen la suite como un método del fixture
#include <cppunit/ui/text/TestRunner.h> //el resto, idem... int main( int argc, char **argv){ CppUnit::TestSuite *suite=new CppUnit::TestSuite("MiTest"); suite->addTest(new CppUnit::TestCaller<MiTestFixture> ("metodoTest",&MiTestFixture::metodoTest)); CppUnit::TextUi::TestRunner runner; runner.addTest(suite); runner.run(); }
Y ahora sí hay un output
. OK (1 tests)
Y es que el Runner es el encargado del output. De hecho ahora veo que también podría haberlo hecho sólo con el caller
CppUnit::TextUi::TestRunner runner; runner.addTest(new CppUnit::TestCaller<MiTestFixture> ("metodoTest",&MiTestFixture::metodoTest)); runner.run();
O sólo con el TestCase
CppUnit::TextUi::TestRunner runner; runner.addTest(new MiTest()); runner.run();
Que es lo que faltó explicar en el tutorial…
Lo que falta saber de CppUnit son las macros, que son para simplificar todo ésto, en particular la creación de la suite desde el fixture. Y lo otro es el TestFactoryRegistry, que es para agregar las suites al runner en tiempo de ejecución.
Google Test
Sino, otra opción es usar Google Test >> que puede que tenga un comportamiento más automatizado así como un conjunto de assertions más interesante (en particular investigar los death tests, que pueden servir para probar que un algoritmo no ‘se quede pegado’).
compilarla
Para compilar >> Google Test, después de bajarlo, descomprimimos la carpeta y le doy
./configure
checkea los requerimientos y crea los Makefile, en seguida,
make
compila, pero ¿dónde deja las librerías? Si intento instalarlo como shared library
sudo make install
no me deja:
'make install' is dangerous and not supported. Instead, see README for how to integrate Google Test into your build system.
y efectivamente algo había leído de que los flags (ej. rutas a fuentes, etc.) dependen de cada proyecto. Aers.
README dice que debo crear un library build target (¿será lo mismo que un make target >>?) para compilar el archivo gtest-all.cc (que incluye todos los demás), con las carpetas ${GTEST_DIR}/include y ${GTEST_DIR} en el header search path. Siguiendo las instrucciones y sin cachar mucho, ejecuto (obviamente, GTEST_DIR es el directorio donde descomprimí)
g++ -I${GTEST_DIR}/include -I${GTEST_DIR} -c ${GTEST_DIR}/src/gtest-all.cc
La siguiente línea, si no me engaño, genera a partir de eso la librería estática
ar -rv libgtest.a gtest-all.o
Que de hecho es lo mismo que hace make (que habíamos ejecutado antes) dentro de la carpeta GTEST_DIR/make… o sea es cómo funciona internamente. La pregunta es entonces, nuevamente >>, como usar la librería estática desde nuestro proyecto.
Si no me equivoco, la librería está pensada para meterla dentro del proyecto descompilada e ir agregándole pruebas en el Makefile… En todo caso, lo que éste estaría haciendo es incluir los fuentes de google test en el search path y empaquetar las pruebas junto con la librería estática:
g++ -I${GTEST_DIR}/include path/to/your_test.cc libgtest.a -o your_test
Mmm, ok. Entonces, ¿cómo hago mi propia prueba?
usarla
Lo primero es la instrucción -I, para que el compilador incluya todos los headers en la carpeta include de la librería. En CDT sería.
Luego el linker tiene que enlazar -l a la librería libgtest.a (¡sin lib y .a!), para lo cual tiene que saber encontrarla, dándole el search path -L. En CDT nuevamente:
(Nótese que puede que necesite también incluír la librería pthread)
Y compila… luego tengo que traerme el header principal para poder definir un testmain
#include<gtest/gtest.h> TEST(TestTest, TestAlgo){ EXPECT_EQ(1, 1); }
Para correrlo, hay que darle un método main que lo incializa y corre los tests (también se puede linkear el archivo src/gtest_main.cc)
int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
Y efectivamente el test corre
[==========] Running 1 test from 1 test case. [----------] Global test environment set-up. [----------] 1 test from TestTest [ RUN ] TestTest.TestAlgo [ OK ] TestTest.TestAlgo (0 ms) [----------] 1 test from TestTest (0 ms total) [----------] Global test environment tear-down [==========] 1 test from 1 test case ran. (1 ms total) [ PASSED ] 1 test.
Ahora, este método main colide con el main de la aplicación… entonces ¿cómo indicar que esto se debe correr al momento de hacer tests? En el fondo lo que pasa es que las pruebas se deben hacer en un nuevo proyecto que importe los headers que necesite del mío.
La idea era comparar. En este primer vistazo, diría que GoogleTest es harto más simple que CppUnit, pero que requiere de más configuración. Para más consideraciones, ver ésto >>