Iteracyjny test jednostkowy metody klasy

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 1496 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:

// Pierwsza klasa do przetestowania class IterableChangeDisplay : public IIterableChangeValueModelTesting{ public: virtual void iterableChengeDisplay(uint32_t &start, uint32_t &end, uint8_t &wsp){ if(wsp){ start = std::ceil(qreal(end - start) / wsp) + start; }else{ start = end; } } }; // Druga klasa do przetestowania class IterableChangeDisplay2 : public IIterableChangeValueModelTesting{ public: virtual void iterableChengeDisplay(uint32_t &start, uint32_t &end, uint8_t &wsp){ if(wsp){ start = (end - start) / wsp + start; }else{ start = end; } } };

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

class IIterableChangeValueModelTesting : public IIterableChangeValueModel, ITesting{ public: virtual void testing(enum testing_flags flag = testing_flags::DISPLAY_ALL, QHtmlObject* html = 0, std::unordered_map<QString, long> *parameters = 0){ uint32_t start = 100; uint32_t end = 1000; uint8_t wsp = 100; QHtmlObject* body = 0; if(html){ body = html->getElementsByName("body")[0]; } if(body) body ->addHtmlChild(QHtmlObject::createH1("Testing class: " + QString(typeid(*this).name()))); QString data = "=======================================================\nTesting class: " + QString(typeid(*this).name()) + "\n" "=======================================================\n\n"; qDebug() << data; for(wsp = 0; wsp <= 100; wsp ++){ start = 100; if(parameters){ auto search = parameters->find("start"); if(search != parameters->end()){ start = search->second; } search = parameters->find("end"); if(search != parameters->end()){ end = search->second; } } if(body) body ->addHtmlChild(QHtmlObject::createH2("Testing for parameters:")) .addHtmlChild(QHtmlObject::createParagraph("start = " + QString::number(start))) .addHtmlChild(QHtmlObject::createParagraph("end = " + QString::number(end))) .addHtmlChild(QHtmlObject::createParagraph("wsp = " + QString::number(wsp))); data = "Testing for parameters:\n" "start = " + QString::number(start) + "\n" "end = " + QString::number(end) + "\n" "wsp = " + QString::number(wsp) + "\n"; qDebug() << data; for(int i = 0; i < 1000; i++){ iterableChengeDisplay(start, end, wsp); if(start == end){ if(flag == testing_flags::DISPLAY_ALL){ if(body) body ->addHtmlChild( QHtmlObject::createH3("Looks like algorythm works fine! End parameters for iteration: " + QString::number(i + 1) + ":") .addAttributes("class", "good") ) .addHtmlChild(QHtmlObject::createParagraph("start = " + QString::number(start))) .addHtmlChild(QHtmlObject::createParagraph("end = " + QString::number(end))) .addHtmlChild(QHtmlObject::createParagraph("wsp = " + QString::number(wsp))); data = "Looks like algorythm works fine! End parameters:\n" "Parameters for iteration nr. " + QString::number(i + 1) + "\n" "start = " + QString::number(start) + "\n" "end = " + QString::number(end) + "\n" "wsp = " + QString::number(wsp) + "\n"; qDebug() << data; } break ; } } if(start != end){ if(body) body ->addHtmlChild( QHtmlObject::createH3("Looks like algorythm not works too good for 1000 iterations:") .addAttributes("class", "bad") ) .addHtmlChild(QHtmlObject::createParagraph("start = " + QString::number(start))) .addHtmlChild(QHtmlObject::createParagraph("end = " + QString::number(end))) .addHtmlChild(QHtmlObject::createParagraph("wsp = " + QString::number(wsp))); data = "Looks like algorythm not works too good for 1000 iterations:\n" "start = " + QString::number(start) + "\n" "end = " + QString::number(end) + "\n" "wsp = " + QString::number(wsp) + "\n"; qDebug() << data; } } } };

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

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.

class ITesting{ public: virtual void testing(enum testing_flags flag = testing_flags::DISPLAY_ALL, QHtmlObject* html = 0, std::unordered_map<QString, long> *parameters = 0) = 0; virtual ~ITesting(){}; }; class IIterableChangeValueModel{ public: virtual void iterableChengeDisplay(uint32_t &start, uint32_t &end, uint8_t &wsp) = 0; virtual ~IIterableChangeValueModel(){} };

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:

enum testing_flags{ DISPLAY_ERROR_ONLY, DISPLAY_ALL };

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:

IterableChangeDisplay2 model2; IterableChangeDisplay model1; QFile file("test.html"); file.open(QIODevice::WriteOnly); QTextStream textStream(&file); std::unordered_map<QString, long> parameters = {{"start", 0}, {"end", 500}}; parameters.insert( std::make_pair<QString, long>("wsp", 100) ); QHtmlPage html("Testing raport"); html.addLocalStyle( ".bad{ color: red;} .good{ color: green; }"); model2.testing(testing_flags::DISPLAY_ALL, &html, ¶meters); model1.testing(testing_flags::DISPLAY_ALL, &html, ¶meters); textStream << html; 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