18 zut Testowanie QT + Google Test

Instalowanie QT na Linux

1) Ściągnij wersję instalacyjną z sieci:  Link
lub wersję qt-opensource-linux-x64-5.11.2


2) Nadaj prawa wykonywania plikowi





3) uruchom plik instalacyjny
4) wybierz dodatkowo Desktop gcc 64bit




5) zaakceptuj licencję
6) ściągnij zip z git google test https://github.com/google/googletest rozpakuje do katalogu.

7) uruchom QT i daj nowy projekt/ Other Projekt/ Auto Test Project 






8) wybierz Test Framework - Googletest




9) nazwij projekt i ustaw katalog gdzie rozpakowałeś Google Test z Gita  (Browse)




Masz skonfigurowany Google Test w QT i utworzony projekt




Tworzenie Testów 

wygląd projektu



plik main uruchamia test. Testy dopisujesz w pliku tst_t1.h   (Headers)

dodaj swój plik z definicją funkcji/klasy typu cpp do Source (prawy mouse Add existing Files)

pojawi się Folder pod Source i dołączony plik, tu kalkulator.cpp    (1) 





Wejdź na tst_t1.h   (2) 

dodaj include z plikiem z prototypem twojej funkcji klasy (typ h) podaj ścieżkę,tu folder wyżej ../
(3)


W miejscu makra TEST piszesz testy.
wywołujesz swoją funkcję z konkretnymi wartościami kalkulator ( 2, 3)  = wynik

i porównujesz poleceniami google test tu ASSERT_EQ(war1, war2)  (4) 


polecenia:

Binary Comparison

This section describes assertions that compare two values.
Fatal assertionNonfatal assertionVerifies
ASSERT_EQ(val1, val2);EXPECT_EQ(val1, val2);val1 == val2
ASSERT_NE(val1, val2);EXPECT_NE(val1, val2);val1 != val2
ASSERT_LT(val1, val2);EXPECT_LT(val1, val2);val1 < val2
ASSERT_LE(val1, val2);EXPECT_LE(val1, val2);val1 <= val2
ASSERT_GT(val1, val2);EXPECT_GT(val1, val2);val1 > val2
ASSERT_GE(val1, val2);EXPECT_GE(val1, val2);val1 >= val2

expect_eq   vs   assert_eq

when they fail, ASSERT_* yields a fatal failure and returns from the current function, while EXPECT_* yields a nonfatal failure, allowing the function to continue running.

Normally, EXPECT_* is the better option since the rest of the test can continue to run and can give useful output. However, ASSERT_* is better if the test shouldn't continue.



Przykład Tworzenia Mocków

LINK kod z github

Dla prostej klasy Kalkulator, która korzysta z obiektów Dodaj i Odejmij:

hpp
class Kalkulator
{
public:
    Kalkulator(Dodaj& aa,Odejmij& bb);
    int dodaj(int a,int b);
    int odejmij(int a,int b);
private:
    class Dodaj& aa;
    class Odejmij& bb;
};

cpp
#include "kalkulator.h"

namespace kalk {

Kalkulator::Kalkulator(class Dodaj& aa,class Odejmij& bb):aa(aa),bb(bb){}

int Kalkulator::dodaj(int a, int b)
 {
  return aa.dodawanie(a,b);
 }

int Kalkulator::odejmij(int a, int b)
 {
  return bb.odejmowanie(a,b);
 }
}

hpp Dodaj
class Dodaj
{
public:
    Dodaj();
    virtual ~Dodaj();
    virtual int dodawanie(int a, int b);
};

cpp Dodaj
#include "dodaj.h"

Dodaj::Dodaj(){}
Dodaj::~Dodaj() {}

int Dodaj::dodawanie(int a, int b)
 {
  return a+b;
 }


Co ważne, metoda dodawanie z Klasy Dodaj musi być virtualna, ponieważ kiedy tworzymy Mocka (dziedziczącego po Dodaj) musi nadpisać oryginalną metodą (przykład mockowania niewirtualnych później) 

Tworzymy Mocki tych Klas: (przykład dla Dodaj)
dod_mock.h
#ifndef DOD_MOCK_H
#define DOD_MOCK_H

#include "gmock/gmock.h"
#include "../kalkulator/dodaj.h"

namespace mocks{

class Dod_mock:public kalk::Dodaj
{
public:
    Dod_mock();
    virtual ~Dod_mock();

    MOCK_METHOD2( dodawanie, int(int id, int id2));
};

} //namespace mocks{
#endif // DOD_MOCK_H


dod_mock.cpp
#include "dod_mock.h"
mocks::Dod_mock::Dod_mock(){}

mocks::Dod_mock::~Dod_mock(){}


i dodajemy do projektu:

Plik main wygląda standardowo, natomiast plik testowy wygląda tak:

#pragma once
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "dod_mock.h"
#include "od_mock.h"
#include "../kalkulator/kalkulator.h"

using namespace testing;

namespace my_test
{

class Base
{
public:
    Base()
    {
        vDefineDefaultActions();
    }

    virtual ~Base()    {}

    void vDefineDefaultActions()
    {
        // init default action
//       ON_CALL(m_dpool_mock, DPOOL_enSetPoolData(_, _))
//                .WillByDefault(Return(E_DATA_OK));
    }

//dodawanie mockow
        NiceMock<mocks::Dod_mock>  dod_mock;
        NiceMock<mocks::Od_mock>  od_mock;

    };

class KlasaTestowa : public Base, public testing::Test
{
protected:
    virtual void SetUp()
    {
        kalkobj=new kalk::Kalkulator(dod_mock,od_mock);
    }

    virtual void TearDown()
    {
        delete kalkobj;
    }

    kalk::Kalkulator *kalkobj;
};


TEST_F(KlasaTestowa, test1)
{
    EXPECT_CALL(dod_mock, dodawanie(_,_)).WillOnce(Return(9));
    int ret = kalkobj->dodaj(5,4);
    EXPECT_EQ(ret, 9);
}

TEST_F(KlasaTestowa, test2)
{
    EXPECT_CALL(od_mock, odejmowanie(_,_)).Times(2).WillRepeatedly(Return(5));
    kalkobj->odejmij(11,6);
    int ret2 = kalkobj->odejmij(11,6);
    EXPECT_EQ(ret2, 5);
}

} //namespace


Tu przykład TEST_F (nie zwykłego TEST) czyli testów bazujących na klasie w której dla każdego testu będą uruchomione standardowe zachowania:

Klasa Base posiada metodę vDefineDefaultActions  uruchamianą w konstruktorze, która może uruchamiać deflautową akcję (tu wykomentowany przykład). W klasie bazowej również tworzone są obiekty Mocków, tu za pomocą makra NiceMock. 
Stworzone obiekty mocków Dodaj i Odejmij będą później wykorzystane podczas tworzenia obiektu Kalkulator który przyjmuje w konstruktorze referencje na obiekty Dodaj i Odejmij.
KlasaTestowa dziedziczące po (public Base, public testing::Test ) ma dwie metody SetUp która uruchamiana jest na początek testu (tu tworzy obiekt Kalkulatora) i TearDown na koniec (zwalnia pamięć).

TEST_F (Nazwaklasy, nazwatestu)
najpierw wypisujemy czego oczekujemy że będzie wywołane:
EXPECT_CALL(obiektmocka, metoda(argumenty) . polecenie...

argumenty mogą być konkretne, albo "_" wtedy argument nie ma znaczenia
polecenia po kropce: Times (numer) - ile razy ma być wykonane
                                   WillOnce (co ma zwrócić) - raz wykona i mock zwróci to co chcemy 
                                   WillRepeatedly(co ma zwrócić) wykona wielokrotnie, można połączyć z Times

następnie wywołujemy metodę testowanego obiektu (i expect_call będzie sprawdzał, czy wywołanie tej metody uruchomi mocka, następnie mock zwróci to co podalismy powyżej):

int ret = kalkobj->dodaj(5,4);
    EXPECT_EQ(ret, 9);

tu dodatkowo sprawdzana jest poprawność zwracanej wartości.


Dynamic Mock vs Strict Mock vs Stub





SetArgReferee
podsyłanie do argumentów mocka, innych wartosci/obiektow

https://stackoverflow.com/questions/23156717/gmock-multiple-in-out-parameters-setargreferee



Mocking Private or Protected Methods
Mocking Overloaded Methods
Mocking Class Templates
Mocking Nonvirtual Methods
Mocking Free Functions







Linki:

blog Macieja Amiserowicza - LINK 

Google Test dokumentacja github -  LINK

Googletest Primer na github -  link1:

A quick introduction to the Google C++ Testing -  link2:

Google Test Mocks:  - LINK3


Programy wspomagające pisanie testów podczas pisania softu:

NCrunch - LINK

Porównanie Resharper vs. NCrunch vs. VS 2017 - LINK

Bibliotek wspomagająca mockowanie - LINK