Iteracyjny test jednostkowy metody klasy

Stronę tą wyświetlono już: 153 razy

Do celów przykładowych załóżmy, że dla pewnej klasy głównej, która steruje pewnym sygnałem konieczne jest opracowanie modelu jego sterowaniem. Model ten może istnieć w różnych postaciach testowych więc będzie wygodnie opracować klasę dziedziczącą po jednym interfejsie. Oto przykład takiej właśnie klasy:

Listing 1
  1. // Pierwsza klasa do przetestowania
  2. class IterableChangeDisplay : public IIterableChangeValueModelTesting{
  3. public:
  4. virtual void iterableChengeDisplay(uint32_t &start, uint32_t &end, uint8_t &wsp){
  5. if(wsp){
  6. start = std::ceil(qreal(end - start) / wsp) + start;
  7. }else{
  8. start = end;
  9. }
  10. }
  11. };
  12. // Druga klasa do przetestowania
  13. class IterableChangeDisplay2 : public IIterableChangeValueModelTesting{
  14. public:
  15. virtual void iterableChengeDisplay(uint32_t &start, uint32_t &end, uint8_t &wsp){
  16. if(wsp){
  17. start = (end - start) / wsp + start;
  18. }else{
  19. start = end;
  20. }
  21. }
  22. };

Jak widać klasy IterableChangeDisplay i IterableChangeDisplay2 dziedziczą po interfejsie IIterableChangeValueModelTesting, którego konstrukcja jest następująca:

Listing 2
  1. class IIterableChangeValueModelTesting : public IIterableChangeValueModel, ITesting{
  2. public:
  3. virtual void testing(enum testing_flags flag = testing_flags::DISPLAY_ALL, QHtmlObject* html = 0, std::unordered_map<QString, long> *parameters = 0){
  4. uint32_t start = 100;
  5. uint32_t end = 1000;
  6. uint8_t wsp = 100;
  7. QHtmlObject* body = 0;
  8. if(html){
  9. body = html->getElementsByName("body")[0];
  10. }
  11. if(body)
  12. body
  13. ->addHtmlChild(QHtmlObject::createH1("Testing class: " + QString(typeid(*this).name())));
  14. QString data = "=======================================================\nTesting class: " +
  15. QString(typeid(*this).name()) + "\n"
  16. "=======================================================\n\n";
  17. qDebug() << data;
  18. for(wsp = 0; wsp <= 100; wsp ++){
  19. start = 100;
  20. if(parameters){
  21. auto search = parameters->find("start");
  22. if(search != parameters->end()){
  23. start = search->second;
  24. }
  25. search = parameters->find("end");
  26. if(search != parameters->end()){
  27. end = search->second;
  28. }
  29. }
  30. if(body)
  31. body
  32. ->addHtmlChild(QHtmlObject::createH2("Testing for parameters:"))
  33. .addHtmlChild(QHtmlObject::createParagraph("start = " + QString::number(start)))
  34. .addHtmlChild(QHtmlObject::createParagraph("end = " + QString::number(end)))
  35. .addHtmlChild(QHtmlObject::createParagraph("wsp = " + QString::number(wsp)));
  36. data = "Testing for parameters:\n"
  37. "start = " + QString::number(start) + "\n"
  38. "end = " + QString::number(end) + "\n"
  39. "wsp = " + QString::number(wsp) + "\n";
  40. qDebug() << data;
  41. for(int i = 0; i < 1000; i++){
  42. iterableChengeDisplay(start, end, wsp);
  43. if(start == end){
  44. if(flag == testing_flags::DISPLAY_ALL){
  45. if(body)
  46. body
  47. ->addHtmlChild(
  48. QHtmlObject::createH3("Looks like algorythm works fine! End parameters for iteration: " + QString::number(i + 1) + ":")
  49. .addAttributes("class", "good")
  50. )
  51. .addHtmlChild(QHtmlObject::createParagraph("start = " + QString::number(start)))
  52. .addHtmlChild(QHtmlObject::createParagraph("end = " + QString::number(end)))
  53. .addHtmlChild(QHtmlObject::createParagraph("wsp = " + QString::number(wsp)));
  54. data = "Looks like algorythm works fine! End parameters:\n"
  55. "Parameters for iteration nr. " + QString::number(i + 1) + "\n"
  56. "start = " + QString::number(start) + "\n"
  57. "end = " + QString::number(end) + "\n"
  58. "wsp = " + QString::number(wsp) + "\n";
  59. qDebug() << data;
  60. }
  61. break ;
  62. }
  63. }
  64. if(start != end){
  65. if(body)
  66. body
  67. ->addHtmlChild(
  68. QHtmlObject::createH3("Looks like algorythm not works too good for 1000 iterations:")
  69. .addAttributes("class", "bad")
  70. )
  71. .addHtmlChild(QHtmlObject::createParagraph("start = " + QString::number(start)))
  72. .addHtmlChild(QHtmlObject::createParagraph("end = " + QString::number(end)))
  73. .addHtmlChild(QHtmlObject::createParagraph("wsp = " + QString::number(wsp)));
  74. data = "Looks like algorythm not works too good for 1000 iterations:\n"
  75. "start = " + QString::number(start) + "\n"
  76. "end = " + QString::number(end) + "\n"
  77. "wsp = " + QString::number(wsp) + "\n";
  78. qDebug() << data;
  79. }
  80. }
  81. }
  82. };

Wewnątrz interfejsu IIterableChangeValueModelTesting znajduje się jedynie metoda testing czysto wirtualna, która pozwala na przeprowadzenie testu działania algorytmu dla wartości:

  • wsp - zawierającej się w zakresie od 0 do 100;
  • start - ustawionej domyślnie na wartość 100;
  • end - ustawionej domyślnie na wartość 1000;
  • i - wewnętrznie określona liczba iteracji na 1000

Metoda testing tworzy plik .html z raportem o przebiegu testowania wyświetlając równocześnie te dane na ekranie. Jak widać interfejs ten dziedziczy po dwóch innych interfejsach: IIterableChangeValueModel - dostarcza czysto wirtualnej metody przeznaczonej do modyfikacji sygnału; ITesting - dostarcza czysto wirtualnej metody do testowania funkcji modyfikacji sygnału.

Listing 3
  1. class ITesting{
  2. public:
  3. virtual void testing(enum testing_flags flag = testing_flags::DISPLAY_ALL, QHtmlObject* html = 0, std::unordered_map<QString, long> *parameters = 0) = 0;
  4. virtual ~ITesting(){};
  5. };
  6. class IIterableChangeValueModel{
  7. public:
  8. virtual void iterableChengeDisplay(uint32_t &start, uint32_t &end, uint8_t &wsp) = 0;
  9. virtual ~IIterableChangeValueModel(){}
  10. };

Klasa QHtmlObject użyta jako parametr metody testing została napisana przeze mnie do generowania kodu HTML ponieważ na moje potrzeby wolę tworzyć plik raportu w bardziej przystępnej i sformatowanej formie. Pierwszy parametr wcześniej wspomnianej metody przyjmuje wartość jednej z dostępnych flag:

Listing 4
  1. enum testing_flags{
  2. DISPLAY_ERROR_ONLY,
  3. DISPLAY_ALL
  4. };

Klasa IterableChangeDisplay2 ma błąd wewnętrzny nie uwzględniający działania na liczbach całkowitych, natomiast klasa IterableChangeDisplay już takiego błędu jest pozbawiona. Oto kod wykorzystujący system testujący do obu tych klas:

Listing 5
  1. IterableChangeDisplay2 model2;
  2. IterableChangeDisplay model1;
  3. QFile file("test.html");
  4. file.open(QIODevice::WriteOnly);
  5. QTextStream textStream(&file);
  6. std::unordered_map<QString, long> parameters = {{"start", 0}, {"end", 500}};
  7. parameters.insert( std::make_pair<QString, long>("wsp", 100) );
  8. QHtmlPage html("Testing raport");
  9. html.addLocalStyle( ".bad{ color: red;} .good{ color: green; }");
  10. model2.testing(testing_flags::DISPLAY_ALL, &html, ¶meters);
  11. model1.testing(testing_flags::DISPLAY_ALL, &html, ¶meters);
  12. textStream << html;
  13. file.close();

Raport, jaki powyższy kod utworzy jest dość długi, ale dla celów poglądowych zamieszczam go tutaj. Jak wynika z tegoż raportu test dla obiektu klasy IterableChangeDisplay2 zakończył się powodzeniem jedynie w dwóch pierwszych przypadkach, a to dlatego, że nie uwzględniono operacji na liczbach całkowitych. Ten sam test dla obiektu klasy IterableChangeDisplay kończy się pomyślnie dla wszystkich testowanych wartości.

Diagram UML systemu testującego
Rys. 1
Diagram UML systemu testującego

Komentarze