3. C++ et le monde extérieur




3.8. Tests

Avant de pouvoir être mis en production les programmes doivent être testés afin d'établir leur fiabilité et leur tolérance aux fautes.

On recommande aux programmeurs de mettre en place des tests qui ont pour but de débusquer les bogues inhérants à chaque programme.

En Génie Logiciel notamment une bonne pratique de développement est le Développement Dirigé par les Tests (Test Driven Development) qui consiste à développer les programmes de tests avant d'écrire les classes de l'application.

Il existe différents types de tests, on peut citer par exemple :

Pour réaliser les tests unitaires on peut utiliser :

Le principe de fonctionnement est le suivant : on crée des classes de type TestCase/TestFixture qui sont composées d'un ensemble de méthodes de test. Ces méthodes seront exécutées pour valider les tests et on dispose de macros qui permettent d'assurer la validité du comportement d'un test : CPPUNIT_ASSERT/EXPECT_EQ.

3.8.1. Framework CPPUnit

3.8.1.a  installation

Sous Ubuntu 14.04 installer les librairies libcppunit-1.13-0 et libcppunit-dev ou télécharger la dernière version sur freedesktop.org.

3.8.1.b  première version

Voici un exemple de base qui introduit des méthodes de test pour la classe vector de la STL :

Par défaut, les méthodes suivantes héritées de CppUnit::TestFixture permettent

A l'intérieur de chaque méthode de test on utilise la macro instruction CPPUNIT_ASSERT qui permet de vérifier si une condition est validée ou non.

Il existe d'autres assertions, comme :

  1. #ifndef TEST_VECTOR_H
  2. #define TEST_VECTOR_H
  3.  
  4. #include "cppunit/TestCase.h"
  5. #include "cppunit/TestFixture.h"
  6. #include "cppunit/TestCaller.h"
  7. #include "cppunit/TestResult.h"
  8. #include "cppunit/TestSuite.h"
  9. #include "cppunit/CompilerOutputter.h"
  10. #include "cppunit/XmlOutputter.h"
  11. #include "cppunit/ui/text/TestRunner.h"
  12.  
  13. #include <vector>
  14. using namespace std;
  15.  
  16.  
  17. class TestFixture : public CppUnit::TestFixture {
  18. private:
  19.     vector<int> v;
  20.    
  21. public:
  22.     // methods inherited from TestFixture
  23.     void setUp();
  24.     void tearDown();
  25.  
  26.     // test methods
  27.     void test_push_back();
  28.     void test_erase();
  29.     void test_erase_insert();
  30.     void test_fail1();
  31.     void test_fail2();
  32. };
  33.  
  34. #endif /* TEST_VECTOR_H */
  35.  
  1. /*
  2.  * test_vector.cpp
  3.  */
  4.  
  5. #include "test_vector_0.h"
  6. #include <algorithm>
  7. #include <iterator>
  8. #include <numeric>
  9. using namespace std;
  10.  
  11. // ----------------------------------------------
  12.  
  13. const int MAX_VALUES = 100;
  14.  
  15. /**
  16.  * setUp: function called before each test
  17.  */
  18. void TestFixture::setUp() {
  19.     for (int i=1; i<=MAX_VALUES; ++i) {
  20.         v.push_back(i);
  21.     }
  22. }
  23.  
  24. /**
  25.  * setUp: function called after each test
  26.  */
  27. void TestFixture::tearDown() {
  28.     v.clear();
  29. }
  30.  
  31. /**
  32.  * test that all values are present by computing
  33.  * sum i=1,size of v[i] which is supposed to be
  34.  * equal to size*(size+1)/2
  35.  */
  36. void TestFixture::test_push_back() {
  37.     // vector has been filled by method 'setUp'
  38.     int total = accumulate(v.begin(), v.end(), 0);
  39.    
  40.     int expected_total = (MAX_VALUES * (MAX_VALUES+1)) / 2;
  41.    
  42.     CPPUNIT_ASSERT(total == expected_total);   
  43. }
  44.  
  45. /**
  46.  * test that the value erased are not present
  47.  */
  48. void TestFixture::test_erase() {
  49.     // vector has been filled by method 'setUp'
  50.    
  51.     // remove first and last values
  52.     v.erase(v.begin());
  53.     v.erase(v.end()-1);
  54.    
  55.     int total = accumulate(v.begin(), v.end(), 0);
  56.    
  57.     int expected_total = (MAX_VALUES * (MAX_VALUES+1)) / 2;
  58.     expected_total -= (1 + MAX_VALUES);
  59.    
  60.     CPPUNIT_ASSERT(total == expected_total);
  61. }
  62.  
  63. /**
  64.  * test erase followed by insert of the value
  65.  *
  66.  * randomly select values, remove them and reinsert them
  67.  *
  68.  */
  69. void TestFixture::test_erase_insert() {
  70.     // vector has been filled by method 'setUp'
  71.     for (int i=0; i<v.size(); ++i) {
  72.         int index = rand() % v.size();
  73.         int value = v[index];
  74.         v.erase(v.begin() + index);
  75.         v.insert(v.begin() + rand() % v.size(), value);
  76.    
  77.     }
  78.     int total = accumulate(v.begin(), v.end(), 0);
  79.    
  80.     int expected_total = (MAX_VALUES * (MAX_VALUES+1)) / 2;
  81.    
  82.     CPPUNIT_ASSERT(total == expected_total);
  83. }
  84.  
  85. /**
  86.  * Test that will fail, used for example purpose
  87.  */
  88. void TestFixture::test_fail1() {
  89.     CPPUNIT_ASSERT(0 == 1);
  90. }
  91.  
  92. /**
  93.  * Test that will fail, used for example purpose
  94.  */
  95. void TestFixture::test_fail2() {
  96.     CPPUNIT_ASSERT(true == false);
  97. }
  98.  
  99.  
  100. /**
  101.  * declare suite of tests
  102.  *
  103.  */
  104. CppUnit::TestSuite *make_suite() {
  105.     // give a name to the TestSuite
  106.     CppUnit::TestSuite *suite = new CppUnit::TestSuite("vector");
  107.     cout << "==============================================" << endl;
  108.     cout << "TEST " << suite->getName() << " (" << __FILE__ << ")" << endl;
  109.     cout << "==============================================" << endl;
  110.  
  111.     // record the methods that needs to be executed
  112.     suite->addTest(new CppUnit::TestCaller<TestFixture>("test_push_back",
  113.         &TestFixture::test_push_back));
  114.     suite->addTest(new CppUnit::TestCaller<TestFixture>("test_erase",
  115.         &TestFixture::test_erase));
  116.     suite->addTest(new CppUnit::TestCaller<TestFixture>("test_erase_insert",
  117.         &TestFixture::test_erase_insert));
  118.     suite->addTest(new CppUnit::TestCaller<TestFixture>("test_fail1",
  119.         &TestFixture::test_fail1));
  120.     suite->addTest(new CppUnit::TestCaller<TestFixture>("test_fail2",
  121.         &TestFixture::test_fail2));
  122.  
  123.     return suite;
  124. }
  125.  
  126. /**
  127.  * main function
  128.  */
  129. int main(int argc, char *argv[]) {
  130.     CppUnit::TextUi::TestRunner runner;
  131.     CppUnit::XmlOutputter *xml_outputter = NULL;
  132.  
  133.     // create suite
  134.     CppUnit::TestSuite *suite = make_suite();
  135.     runner.addTest(suite);
  136.  
  137.     // set output format as text
  138.     runner.setOutputter(new CppUnit::CompilerOutputter(&runner.result(), cout));
  139.    
  140.     // run all tests
  141.     runner.run();
  142.    
  143.     // output as XML also
  144.     ofstream xml_out("output.xml");
  145.     xml_outputter = new CppUnit::XmlOutputter(&runner.result(), xml_out);
  146.     xml_outputter->write();
  147.     xml_out.close();
  148.    
  149.     return 0;
  150. }
  151.  
  152.  

3.8.1.c  utilisation simplifiée

Afin de simplifier la déclaration et l'enregistrement des méthodes de tests au niveau du TestSuite, on crée des macro-instructions dans le fichier header :

  1. #ifndef TEST_VECTOR_H
  2. #define TEST_VECTOR_H
  3.  
  4. #include "cppunit/TestCase.h"
  5. #include "cppunit/TestFixture.h"
  6. #include "cppunit/TestCaller.h"
  7. #include "cppunit/TestResult.h"
  8. #include "cppunit/TestSuite.h"
  9. #include "cppunit/CompilerOutputter.h"
  10. #include "cppunit/XmlOutputter.h"
  11. #include "cppunit/ui/text/TestRunner.h"
  12.  
  13. #include <vector>
  14. using namespace std;
  15.  
  16. // Use those macros and repeat the class name three times in
  17. // CLASS_NAME, CLASS_NAME_STRING
  18. #define CLASS_NAME vector
  19. #define CLASS_NAME_STRING "vector"
  20. #define OUTPUT_XML_FILE "output.xml"
  21.  
  22.  
  23. #define TEST_DECL(x) void test_##x()
  24. #define TEST_ADD(name) \
  25.     suite->addTest(new CppUnit::TestCaller<TestFixture>("test_"#name, \
  26.         &TestFixture::test_##name));
  27.  
  28.  
  29. class TestFixture : public CppUnit::TestFixture {
  30. private:
  31.     vector<int> v;
  32.    
  33. public:
  34.     void setUp();
  35.     void tearDown();
  36.  
  37.  
  38.     TEST_DECL(push_back);
  39.     TEST_DECL(erase);
  40.     TEST_DECL(erase_insert);
  41.     TEST_DECL(fail1);
  42.     TEST_DECL(fail2);
  43. };
  44.  
  45. #endif /* TEST_VECTOR_H */
  46.  
  1. /*
  2.  * test_vector.cpp
  3.  */
  4.  
  5. #include "test_vector_1.h"
  6. #include <algorithm>
  7. #include <iterator>
  8. #include <numeric>
  9. using namespace std;
  10.  
  11. // ----------------------------------------------
  12.  
  13. const int MAX_VALUES = 100;
  14.  
  15. /**
  16.  * setUp: function called before each test
  17.  */
  18. void TestFixture::setUp() {
  19.     for (int i=1; i<=MAX_VALUES; ++i) {
  20.         v.push_back(i);
  21.     }
  22. }
  23.  
  24. /**
  25.  * setUp: function called after each test
  26.  */
  27. void TestFixture::tearDown() {
  28.     v.clear();
  29. }
  30.  
  31. /**
  32.  * test that all values are present by computing
  33.  * sum i=1,size of v[i] which is supposed to be
  34.  * equal to size*(size+1)/2
  35.  */
  36. void TestFixture::test_push_back() {
  37.     // vector has been filled by method 'setUp'
  38.     int total = accumulate(v.begin(), v.end(), 0);
  39.    
  40.     int expected_total = (MAX_VALUES * (MAX_VALUES+1)) / 2;
  41.    
  42.     CPPUNIT_ASSERT(total == expected_total);   
  43. }
  44.  
  45. /**
  46.  * test that the value erased are not present
  47.  */
  48. void TestFixture::test_erase() {
  49.     // vector has been filled by method 'setUp'
  50.    
  51.     // remove first and last values
  52.     v.erase(v.begin());
  53.     v.erase(v.end()-1);
  54.    
  55.     int total = accumulate(v.begin(), v.end(), 0);
  56.    
  57.     int expected_total = (MAX_VALUES * (MAX_VALUES+1)) / 2;
  58.     expected_total -= (1 + MAX_VALUES);
  59.    
  60.     CPPUNIT_ASSERT(total == expected_total);
  61. }
  62.  
  63. /**
  64.  * test erase followed by insert of the value
  65.  *
  66.  * randomly select values, remove them and reinsert them
  67.  *
  68.  */
  69. void TestFixture::test_erase_insert() {
  70.     // vector has been filled by method 'setUp'
  71.     for (int i=0; i<v.size(); ++i) {
  72.         int index = rand() % v.size();
  73.         int value = v[index];
  74.         v.erase(v.begin() + index);
  75.         v.insert(v.begin() + rand() % v.size(), value);
  76.    
  77.     }
  78.     int total = accumulate(v.begin(), v.end(), 0);
  79.    
  80.     int expected_total = (MAX_VALUES * (MAX_VALUES+1)) / 2;
  81.    
  82.     CPPUNIT_ASSERT(total == expected_total);
  83. }
  84.  
  85. /**
  86.  * Test that will fail, used for example purpose
  87.  */
  88. void TestFixture::test_fail1() {
  89.     CPPUNIT_ASSERT(0 == 1);
  90. }
  91.  
  92. /**
  93.  * Test that will fail, used for example purpose
  94.  */
  95. void TestFixture::test_fail2() {
  96.     CPPUNIT_ASSERT(true == false);
  97. }
  98.  
  99.  
  100. /**
  101.  * declare suite of tests
  102.  *
  103.  */
  104. CppUnit::TestSuite *make_suite() {
  105.     CppUnit::TestSuite *suite = new CppUnit::TestSuite(CLASS_NAME_STRING);
  106.     cout << "==============================================" << endl;
  107.     cout << "TEST " << suite->getName() << " (" << __FILE__ << ")" << endl;
  108.     cout << "==============================================" << endl;
  109.  
  110.     TEST_ADD(push_back);
  111.     TEST_ADD(erase);
  112.     TEST_ADD(erase_insert);
  113.     TEST_ADD(fail1);
  114.     TEST_ADD(fail2);
  115.  
  116.     return suite;
  117. }
  118.  
  119. /**
  120.  * main function
  121.  */
  122. int main(int argc, char *argv[]) {
  123.     CppUnit::TextUi::TestRunner runner;
  124.     CppUnit::XmlOutputter *xml_outputter = NULL;
  125.  
  126.     // create suite
  127.     CppUnit::TestSuite *suite = make_suite();
  128.     runner.addTest(suite);
  129.  
  130.     // set output format as text
  131.     runner.setOutputter(new CppUnit::CompilerOutputter(&runner.result(), cout));
  132.    
  133.     // run all tests
  134.     runner.run();
  135.    
  136.     // output as XML also
  137.     ofstream xml_out(OUTPUT_XML_FILE);
  138.     xml_outputter = new CppUnit::XmlOutputter(&runner.result(), xml_out);
  139.     xml_outputter->write();
  140.     xml_out.close();
  141.    
  142.     return 0;
  143. }
  144.  
  145.  

3.8.1.d  compilation, exécution

On compile avec les options suivantes :

g++ -o test_vector_1.exe test_vector_1.cpp -lcppunit

Puis on exécute en lançant le programme, voici le résultat de la sortie en mode texte :

./test_vector_1.exe
==============================================
TEST vector (test_vector_1.cpp)
==============================================
....F.F

test_vector_1.cpp:89:Assertion
Test name: test_fail1
assertion failed
- Expression: 0 == 1

test_vector_1.cpp:96:Assertion
Test name: test_fail2
assertion failed
- Expression: true == false

Failures !!!
Run: 5   Failure total: 2   Failures: 2   Errors: 0

Le programme reporte les assertions non vérifiées :

Voici la sortie au format XML :

  1. <?xml version="1.0" encoding='ISO-8859-1' standalone='yes' ?>
  2. <TestRun>
  3.   <FailedTests>
  4.     <FailedTest id="4">
  5.       <Name>test_fail1</Name>
  6.       <FailureType>Assertion</FailureType>
  7.       <Location>
  8.         <File>test_vector_0.cpp</File>
  9.         <Line>89</Line>
  10.       </Location>
  11.       <Message>assertion failed
  12. - Expression: 0 == 1
  13. </Message>
  14.     </FailedTest>
  15.     <FailedTest id="5">
  16.       <Name>test_fail2</Name>
  17.       <FailureType>Assertion</FailureType>
  18.       <Location>
  19.         <File>test_vector_0.cpp</File>
  20.         <Line>96</Line>
  21.       </Location>
  22.       <Message>assertion failed
  23. - Expression: true == false
  24. </Message>
  25.     </FailedTest>
  26.   </FailedTests>
  27.   <SuccessfulTests>
  28.     <Test id="1">
  29.       <Name>test_push_back</Name>
  30.     </Test>
  31.     <Test id="2">
  32.       <Name>test_erase</Name>
  33.     </Test>
  34.     <Test id="3">
  35.       <Name>test_erase_insert</Name>
  36.     </Test>
  37.   </SuccessfulTests>
  38.   <Statistics>
  39.     <Tests>5</Tests>
  40.     <FailuresTotal>2</FailuresTotal>
  41.     <Errors>0</Errors>
  42.     <Failures>2</Failures>
  43.   </Statistics>
  44. </TestRun>
  45.  

On peut ensuite extraire les méthodes qui ont échoué grâce à un utilitaire comme xpath du package libxml-xpath-perl :

sudo apt-get install libxml-xpath-perl
> xpath -q -e "//FailedTest/Name/text()" output.xml
test_fail1
test_fail2

Voici un script shell qui permet de récupérer les noms des méthodes qui ont échoué tout en vérifiant que tous les tests ont été exécutés :

  1. #!/bin/sh
  2. files=`ls test_*_1.exe`
  3.  
  4. for f in $files ; do
  5.     ./$f >tmp.txt
  6.     x=`xpath -q -e "//FailedTest/Name/text()" output.xml`
  7.     echo "===================";
  8.     echo "$f"
  9.     echo "$x" | awk '{ print "-", $1; }'
  10.     echo "===================";
  11. done
  12.  
> ./cppunit_failures_1.sh 
===================
test_vector_1.exe
- test_fail1
- test_fail2
===================

3.8.1.e  version avancée

Voici une version avancée qui prend en compte le nombre de tests et qui permet de vérifier que l'ensemble des tests à été exécuté.

En effet, si l'une methode échoue et provoque par exemple un segmentation fault, les autres méthodes ne seront pas exécutées et il ne sera pas possible de le savoir. On risque donc de penser que tout va bien alors que certains tests n'ont pas été exécutés.

  1. #ifndef TEST_VECTOR_H
  2. #define TEST_VECTOR_H
  3.  
  4. #include "cppunit/TestCase.h"
  5. #include "cppunit/TestFixture.h"
  6. #include "cppunit/TestCaller.h"
  7. #include "cppunit/TestResult.h"
  8. #include "cppunit/TestSuite.h"
  9. #include "cppunit/CompilerOutputter.h"
  10. #include "cppunit/XmlOutputter.h"
  11. #include "cppunit/ui/text/TestRunner.h"
  12.  
  13. #include <vector>
  14. using namespace std;
  15.  
  16. // Use those macros and repeat the class name three times in
  17. // CLASS_NAME, CLASS_NAME_STRING, OUTPUT_XML_FILE
  18. #define CLASS_NAME vector
  19. #define CLASS_NAME_STRING "vector"
  20. #define OUTPUT_XML_FILE "test_vector.xml"
  21.  
  22.  
  23. #define TEST_DECL(x) void test_##x()
  24. #define ADD_TEST(name) \
  25.     suite->addTest(new CppUnit::TestCaller<TestFixture>("test_"#name, \
  26.         &TestFixture::test_##name));
  27.  
  28.  
  29. class TestFixture : public CppUnit::TestFixture {
  30. private:
  31.     vector<int> v;
  32.    
  33. public:
  34.     void setUp();
  35.     void tearDown();
  36.  
  37.  
  38.     TEST_DECL(push_back);
  39.     TEST_DECL(erase);
  40.     TEST_DECL(erase_insert);
  41.     TEST_DECL(fail1);
  42.     TEST_DECL(fail2);
  43.     TEST_DECL(div_by_zero);
  44. };
  45.  
  46. #endif /* TEST_VECTOR_H */
  47.  
  1. /*
  2.  * test_vector_2.cpp
  3.  */
  4.  
  5. #include "test_vector_2.h"
  6. #include <algorithm>
  7. #include <iterator>
  8. #include <numeric>
  9. using namespace std;
  10. #include <getopt.h>
  11. #include <cstring>
  12. #include <signal.h>
  13. #include <err.h>
  14. #include <cstdlib>
  15. #include <cstdio>
  16.  
  17. void signal_handler(int sig) {
  18.     cerr << endl << "caught signal " << sig << endl;
  19.     cerr.flush();  
  20.     return ;
  21. }
  22.  
  23. void set_signal_handler() {
  24.     signal( SIGABRT, signal_handler);
  25.     signal( SIGHUP, signal_handler);
  26.     signal( SIGILL, signal_handler);
  27.  }
  28.  
  29. // ----------------------------------------------
  30. // description of possible arguments
  31. // that can be used in
  32. // - short format : -m 1
  33. // - or long format : --method=1
  34. // ----------------------------------------------
  35. static struct option long_options[] = {
  36.   {"nbr-tests",         no_argument, 0, 'n'},
  37.   {"output-format",     required_argument, 0, 'f'},
  38.   {0,0,0,0}
  39. };
  40.  
  41. typedef struct {
  42.     string output_file;
  43.     int  output_format;
  44.     bool flag_get_nbr_tests;
  45. } Parameters;
  46.  
  47. Parameters params;
  48.  
  49. const char *params_format_allowed[] = { "text", "xml", NULL };
  50.  
  51. int get_allowed_value(const char *s, const char *tab[]) {
  52.     for (int i=0; tab[i] != NULL; ++i) {
  53.         if (strcmp(s,tab[i])==0) return i;
  54.     }
  55.     return -1;
  56. }
  57.  
  58. void usage(char *prog_name) {
  59.     cout << prog_name << endl;
  60.     cout << "--output-format=text|xml" << endl;
  61.     cout << "--nbr-tests" << endl;
  62.    
  63.     exit(EXIT_FAILURE);
  64. }
  65.  
  66. // ----------------------------------------------
  67.  
  68. const int MAX_VALUES = 100;
  69.  
  70. /**
  71.  * setUp: function called before each test
  72.  */
  73. void TestFixture::setUp() {
  74.     for (int i=1; i<=MAX_VALUES; ++i) {
  75.         v.push_back(i);
  76.     }
  77. }
  78.  
  79. /**
  80.  * setUp: function called after each test
  81.  */
  82. void TestFixture::tearDown() {
  83.     v.clear();
  84. }
  85.  
  86. /**
  87.  * test that all values are present by computing
  88.  * sum i=1,size of v[i] which is supposed to be
  89.  * equal to size*(size+1)/2
  90.  */
  91. void TestFixture::test_push_back() {
  92.     // vector has been filled by method 'setUp'
  93.     int total = accumulate(v.begin(), v.end(), 0);
  94.    
  95.     int expected_total = (MAX_VALUES * (MAX_VALUES+1)) / 2;
  96.    
  97.     CPPUNIT_ASSERT(total == expected_total);   
  98. }
  99.  
  100. /**
  101.  * test that the value erased are not present
  102.  */
  103. void TestFixture::test_erase() {
  104.     // vector has been filled by method 'setUp'
  105.    
  106.     // remove first and last values
  107.     v.erase(v.begin());
  108.     v.erase(v.end()-1);
  109.    
  110.     int total = accumulate(v.begin(), v.end(), 0);
  111.    
  112.     int expected_total = (MAX_VALUES * (MAX_VALUES+1)) / 2;
  113.     expected_total -= (1 + MAX_VALUES);
  114.    
  115.     CPPUNIT_ASSERT(total == expected_total);
  116. }
  117.  
  118. /**
  119.  * test erase followed by insert of the value
  120.  *
  121.  * randomly select values, remove them and reinsert them
  122.  *
  123.  */
  124. void TestFixture::test_erase_insert() {
  125.     // vector has been filled by method 'setUp'
  126.     for (int i=0; i<v.size(); ++i) {
  127.         int index = rand() % v.size();
  128.         int value = v[index];
  129.         v.erase(v.begin() + index);
  130.         v.insert(v.begin() + rand() % v.size(), value);
  131.    
  132.     }
  133.     int total = accumulate(v.begin(), v.end(), 0);
  134.    
  135.     int expected_total = (MAX_VALUES * (MAX_VALUES+1)) / 2;
  136.    
  137.     CPPUNIT_ASSERT(total == expected_total);
  138. }
  139.  
  140. /**
  141.  * Test that will fail, used for example purpose
  142.  */
  143. void TestFixture::test_fail1() {
  144.     CPPUNIT_ASSERT(0 == 1);
  145. }
  146.  
  147. /**
  148.  * Test that will fail, used for example purpose
  149.  */
  150. void TestFixture::test_fail2() {
  151.     CPPUNIT_ASSERT(true == false);
  152. }
  153.  
  154. /**
  155.  * Test that will fail with a division by zero,
  156.  * used for example purpose
  157.  */
  158. void TestFixture::test_div_by_zero() {
  159.     int a = 0;
  160.     int b = 10;
  161.     int c = b / a;
  162.     CPPUNIT_ASSERT(c != 0);
  163. }
  164.  
  165.  
  166. /**
  167.  * declare suite of tests
  168.  *
  169.  */
  170. CppUnit::TestSuite *make_suite() {
  171.     CppUnit::TestSuite *suite = new CppUnit::TestSuite(CLASS_NAME_STRING);
  172.     cout << "==============================================" << endl;
  173.     cout << "TEST " << suite->getName() << " (" << __FILE__ << ")" << endl;
  174.     cout << "==============================================" << endl;
  175.  
  176.     ADD_TEST(push_back);
  177.     ADD_TEST(erase);
  178.     ADD_TEST(erase_insert);
  179.     ADD_TEST(fail1);
  180.     ADD_TEST(fail2);
  181.     ADD_TEST(div_by_zero);
  182.  
  183.     return suite;
  184. }
  185.  
  186. /**
  187.  * main function
  188.  */
  189. int main(int argc, char *argv[]) {
  190.     CppUnit::TextUi::TestRunner runner;
  191.     CppUnit::XmlOutputter *xml_outputter = NULL;
  192.  
  193.     params.output_file = "";
  194.     params.output_format = 0; // text
  195.     params.flag_get_nbr_tests = false;
  196.    
  197.     int option_index;
  198.     while (true) {
  199.         option_index = 0;
  200.         int c = getopt_long(argc, argv, "f:n", long_options, &option_index);
  201.         if (c == -1) break;
  202.  
  203.         switch(c) {
  204.           case 'n':
  205.             params.flag_get_nbr_tests = true;
  206.             break;
  207.           case 'f':
  208.             params.output_format = get_allowed_value(optarg, params_format_allowed);
  209.             break;
  210.         }
  211.     }
  212.    
  213.     if (params.output_format == -1) {
  214.         usage(argv[0]);
  215.     }
  216.    
  217.    
  218.     set_signal_handler();
  219.  
  220.     // create suite
  221.     CppUnit::TestSuite *suite = make_suite();
  222.     runner.addTest(suite);
  223.    
  224.     if (params.output_format == 1) {
  225.         cout << "xml file=" << OUTPUT_XML_FILE << endl;
  226.     }
  227.    
  228.     if (params.flag_get_nbr_tests == true) {
  229.         const std::vector<CppUnit::Test *>& tests = suite->getTests();
  230.         cout << "nbr_tests=" << tests.size() << endl;
  231.         exit(EXIT_SUCCESS);
  232.     }
  233.      
  234.     // set output format as text
  235.     runner.setOutputter(new CppUnit::CompilerOutputter(&runner.result(), cout));
  236.    
  237.     // run all tests
  238.     runner.run();
  239.    
  240.     if (params.output_format == 1) {
  241.         ofstream xml_out(OUTPUT_XML_FILE);
  242.         xml_outputter = new CppUnit::XmlOutputter(&runner.result(), xml_out);
  243.         xml_outputter->write();
  244.         xml_out.close();
  245.     }
  246.    
  247.     return 0;
  248. }
  249.  
  250.  
  1. #!/bin/sh
  2.  
  3. execute_test()
  4. {
  5.     ./$f --output-format=xml >tmp2.txt
  6. }
  7.  
  8. files=`ls test*_2.exe`
  9. for f in $files ; do
  10.     ./$f --nbr-test --output-format=xml >tmp.txt
  11.     nbr=`cat tmp.txt | grep "^nbr_tests=" | cut -d'=' -f2`
  12.    
  13.     # SIGHUP SIGQUIT SIGINT SIGABRT
  14.     echo "execute $f " 
  15.     trap execute_test 1 2 3 6
  16.    
  17.     xml=`cat tmp.txt | grep "^xml file=" | cut -d'=' -f2`
  18.     r=""
  19.     if test -f $xml ; then
  20.         x=`xpath -q -e "//FailedTest/Name/text()" $xml`
  21.         r=`xpath -q -e "//Statistics//Tests/text()" $xml`
  22.         echo "===================";
  23.         if test -n "$x" ; then
  24.             echo "$f"
  25.             echo "$x" | awk '{ print "-", $1; }'
  26.         fi
  27.         if test $nbr != $r ; then
  28.             echo "failed ! $nbr tests expected, only $r performed"
  29.         fi
  30.         echo "===================";
  31.     else
  32.         echo "===================";
  33.         echo "$f FAILURE ! Program did not terminate"
  34.         echo "===================";
  35.     fi
  36. done
  37.  
> ./cppunit_failures_1.sh 
===================
test_vector_1.exe
- test_fail1
- test_fail2
===================

3.8.2. Framework Google Test

Récupérer googletest-release-1.7.0.tar.gz sur googletest, puis dans le répertoire où le fichier a été télécharger. Il n'y a pas (en tout cas dans la version 1.7) de méthode d'installation automatisée. Il faut donc saisir dans le terminal les commandes suivantes :

sudo tar -xvzf googletest-release-1.7.0.tar.gz -C /opt
sudo su
cd /opt/googletest-release-1.7.0
cmake -DBUILD_SHARED_LIBS=ON -Dgtest_build_samples=ON -G "Unix Makefiles" 
make
cp -R include/gtest /usr/include
cp lib*.a /usr/lib 
ldconfig
exit

Pour tester, créer le fichier suivant :

  1. #include <gtest/gtest.h>
  2.  
  3. TEST(MathTest, TwoPlusTwoEqualsFour) {
  4.     EXPECT_EQ(2 + 2, 4);
  5. }
  6.  
  7. TEST(MathTest, ThatWillFail) {
  8.     EXPECT_EQ(0, 1);
  9. }
  10.  
  11.  
  12. int main(int argc, char **argv) {
  13.     ::testing::InitGoogleTest( &argc, argv );
  14.     return RUN_ALL_TESTS();
  15. }
  16.  
  17.  

et compiler avec :

g++ -o my_gtest.exe my_gtest.cpp -lgtest -lgtest_main -lpthread
./my_gtest.exe
[==========] Running 2 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 2 tests from MathTest
[ RUN      ] MathTest.TwoPlusTwoEqualsFour
[       OK ] MathTest.TwoPlusTwoEqualsFour (0 ms)
[ RUN      ] MathTest.ThatWillFail
my_gtest.cpp:8: Failure
Value of: 1
Expected: 0
[  FAILED  ] MathTest.ThatWillFail (0 ms)
[----------] 2 tests from MathTest (0 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test case ran. (0 ms total)
[  PASSED  ] 1 test.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] MathTest.ThatWillFail

avec XML

./my_gtest.exe  --gtest_output="xml:report.xml"
...
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <testsuites tests="2" failures="1" disabled="0" errors="0" timestamp="2015-11-25T11:14:56" time="0.001" name="AllTests">
  3.   <testsuite name="MathTest" tests="2" failures="1" disabled="0" errors="0" time="0.001">
  4.     <testcase name="TwoPlusTwoEqualsFour" status="run" time="0" classname="MathTest" />
  5.     <testcase name="ThatWillFail" status="run" time="0.001" classname="MathTest">
  6.       <failure message="my_gtest.cpp:8&#x0A;Value of: 1&#x0A;Expected: 0" type=""><![CDATA[my_gtest.cpp:8
  7. Value of: 1
  8. Expected: 0]]></failure>
  9.     </testcase>
  10.   </testsuite>
  11. </testsuites>
  12.  

La définition des tests est réalisée grâce à la macro-instruction TEST(NomTestCase, NomMethodeDeTest).