19 Google Test Adv.


MATCHERS:

Eq(20)                               - równy 20
Gt(60)                               - wiekszy niż 60
 HasSubstr("Kowalski")   - zawiera fragment " "
 DoubleEq(3.0)                 - równy dla double
dla kontenerów:
 Contains(18)                    - zawiera 18 (czy kontener zawiera)
Each(Gt(11))                     - czy każdy większy
Pointwise(Eq(), tab2))      - porównuje adresy kontenerow, czy ten sam
ContainerEQ                      - porownuje całę kontenery

AllOf(łączy matchery )

A<typ>()    - sprawdza typ

Field  - pozwala dotrzec do typu

stosuj:                                            (gdzie &DataTab::time określa typstruktury i nazwe pola
(Field(&DataTab::time, Eq(20))

Przykłady:


#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "example.h"
#include <algorithm>
#include <numeric>
#include <functional>


 using namespace ::testing;
//1.Write simple test with matcher, in which:
//  *You will check that pointer ptr points to vector vec
TEST(MatcherTest, containerMatchersType)
{

    ASSERT_THAT(ptr, Pointee(vec));

}


//  *Each elements of vector vec are greater than 100
TEST(MatcherTest, containerMatchersContain)
{

    ASSERT_THAT(vec, Each(Gt(100)));

}


//2. write matcher which:
//  *Will check that time is equal to 20
//  *Will check that temperature is greater then 60
//  *Will check substring "Kowalski" in name string
//  *Will check double value is equal to 3.0
//  *Will check that vector contain value 18
TEST(MatcherTest, containerMatchersTask2)
{


ASSERT_THAT(structToTest.time,Eq(20) );
ASSERT_THAT(structToTest.temperature,Gt(60) );
ASSERT_THAT(structToTest.name, HasSubstr("Kowalski"));
ASSERT_THAT(structToTest.value, DoubleEq(3.0));
ASSERT_THAT(structToTest.vec, Contains(18));


ASSERT_THAT(structToTest, AllOf(Field(&DataTab::time, Eq(20)),
                                Field(&DataTab::temperature, Gt(60)) ,
                                Field(&DataTab::name, HasSubstr("Kowalski")),
                                Field(&DataTab::value, DoubleEq(3.0)),
                                Field(&DataTab::vec, Contains(18))));


}

//3. Compare tab1 and tab2 from example.h and write matcher to find not matching element. Use generateNumbers() method!
TEST(MatcherTest, containerMatchersTask3)
{
generateNumbers();

ASSERT_THAT(tab1, Pointwise(Eq(), tab2));
ASSERT_THAT(tab1, ElementsAreArray (tab2));


}




TEST_F

uruchamia setup klasy testowej, warto je definiować jako struct (publiczna)
metody klasy restowej:
void SetUp()  - ustawia wartości do testó

void TearDown()  - kasuje wartości do testów

w SetUp  tworzymy SUT  - czyli system under Test, instancje testowanej klasy

Jeśli chcemy ustawic setup dla wszystkich testow nie dla pojedynczego z osobna to:

SetUpTestCase()
TearDownTestCase()

uwaga metody statyczne!:



zadania labo01 :


#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "example.h"
#include <algorithm>
#include <numeric>
#include <functional>


 using namespace ::testing;
/* Task 1. Prepare a test class for the DBaseManager. Remember
 * to include a smart pointer to the system under test and (for
 * design reasons) a vector of detailed entries for the manager
 * to manage. In the setup, load the test database from the
 * file lab1_dummydata.txt to MANAGED_DBusing the auxiliary static
 * method DBaseLoader::loadFromFile. Try to not do this in a way
 * resulting in more overhead than needed.
 *
 * Task 2. Extend the test class by adding a matcher for objects
 * of the DBaseSimpleEntry class with the signature:
 * auto matchSimpleDBEntry(const DBaseSimpleEntry & expectedEntry)
 * Make sure to nest predefined matchers within it which are most
 * suitable for the types used in the structure.
 *
 * Task 3. Extend the test class by adding a matcher for objects
 * of the DBaseDetailedEntry class with the signature:
 * auto matchDetailedDBEntry(const DBaseDetailedEntry & expectedEntry)
 */
struct DBaseManagerTest : public Test
{

    static void SetUpTestCase()
    {
        DBaseLoader::loadFromFile("lab1_dummydata.txt", MANAGED_DB);
    }

    void SetUp() override
    {
 
        sut = std::make_unique<DBaseManager>(&MANAGED_DB);

    }
    


    void TearDown() override
    {

    }
    
auto matchSimpleDBEntry(const DBaseSimpleEntry & expectedEntry)
{
 return AllOf(Field(&DBaseSimpleEntry::surname, HasSubstr(expectedEntry.surname)),
               Field(&DBaseSimpleEntry::accountBalance, DoubleEq(expectedEntry.accountBalance)));
}


    std::unique_ptr<DBaseManager> sut;   
    

auto matchDetailedDBEntry(const DBaseDetailedEntry & expectedEntry)
{
    return AllOf(Field(&DBaseDetailedEntry::firstName, HasSubstr(expectedEntry.firstName)),
                 Field(&DBaseDetailedEntry::secondName, HasSubstr(expectedEntry.secondName)),
                 Field(&DBaseDetailedEntry::surname, HasSubstr(expectedEntry.surname)),
                  Field(&DBaseDetailedEntry::accountBalance, DoubleEq(expectedEntry.accountBalance)),
                  Field(&DBaseDetailedEntry::securities, DoubleEq(expectedEntry.securities)),
                  Field(&DBaseDetailedEntry::goldDeposits, Eq(expectedEntry.goldDeposits)),
                 Field(&DBaseDetailedEntry::debt, FloatEq(expectedEntry.debt))
                );

}


    
};

/* Task 4. Test that the getGoldInBank method of DBaseManager
 * works as intended. John wrote that the value of gold deposits
 * in the bank was $5,371,000 on the day it was closed.
 */
TEST_F(DBaseManagerTest, getGoldInBankRetunsSumOfGoldDeposits)
{
    constexpr auto EXPECTED_VALUE=5371000;
    EXPECT_EQ(sut->getGoldInBank(),EXPECTED_VALUE);

}

/* Task 5. Test that the searchBalanceSimple method of DBaseManager
 * works as intended. It should fill the vector of simple enetries
 * passed by refeence as first argument with entries corresponding
 * to customers with an account balance greater or equal to the value
 * passed as second argument. Specify a dummy value (i.e. $50,000)
 * and check that it works as intended.
 *
 * Hint: you don't need to know the exact size of the container
 * to write the unit test.
 */
TEST_F(DBaseManagerTest, searchBalanceSimpleShouldWorkAsIntended)
{

    constexpr double DUMMY_VALUE1=50000;

 std::vector<DBaseSimpleEntry> simpleDB;


 sut->searchBalanceSimple(simpleDB,DUMMY_VALUE1);


   ASSERT_THAT(simpleDB, Contains(Field(&DBaseSimpleEntry::accountBalance, Gt(DUMMY_VALUE1))));

}

/* Task 6. Test that the searchSurnameSimple method of DBaseManager
 * works as intended. Use a dummy string "ill" to test that the method
 * searches the database and fills the vector of simple entries
 * passed by reference as first argument with customer data for
 * customers whose surnames contain "ill". You asked John about
 * such customers and, conveniently, there were only four of them
 * in the test database. The expected customer data output by the
 * method is provided in DUMMY_CONTAINER_01.
 *
 * Hint: use the matchSimpleDBEntry matcher you wrote earlier.
 */
TEST_F(DBaseManagerTest, searchSurnameSimpleOnTestDBReturnsPredefinedCustomersInSimplifiedForm)
{


            std::string DUMMY_VALUE1="ill";

         std::vector<DBaseSimpleEntry> simpleDB;


         sut->searchSurnameSimple(simpleDB,DUMMY_VALUE1);



            ASSERT_THAT(simpleDB, ElementsAre(matchSimpleDBEntry(DUMMY_CONTAINER_01[0]),
                                              matchSimpleDBEntry(DUMMY_CONTAINER_01[1]),
                                              matchSimpleDBEntry(DUMMY_CONTAINER_01[2]),
                                              matchSimpleDBEntry(DUMMY_CONTAINER_01[3])));



}

/* Task 7. Test that the searchSurname method of DBaseManager works
 * as intended. Use a dummy string "Ma" to test that the method
 * searches the database and fills the vector of detailed entries
 * passed by reference as first argument with customer data for
 * customers whose surnames contain "Ma". You asked John about
 * such customers and, conveniently, there were only four of them
 * in the test database. The expected customer data output by the
 * method is provided in DUMMY_CONTAINER_02.
 *
 * Hint: use the matchDetailedDBEntry matcher you wrote earlier.
 */
TEST_F(DBaseManagerTest, searchSurnameOnTestDBReturnsPredefinedCustomers)
{

    std::string DUMMY_VALUE1="Ma";

 std::vector<DBaseDetailedEntry> simpleDB;


 sut->searchSurname(simpleDB,DUMMY_VALUE1);

 /*
 for(auto a : simpleDB )
     std::cout<<a.surname<<"  "<<a.accountBalance<<std::endl;

*/

 ASSERT_THAT(simpleDB, ElementsAre(matchDetailedDBEntry(DUMMY_CONTAINER_02[0]),
                                   matchDetailedDBEntry(DUMMY_CONTAINER_02[1]),
                                   matchDetailedDBEntry(DUMMY_CONTAINER_02[2]),
                                   matchDetailedDBEntry(DUMMY_CONTAINER_02[3])));


}

/* Task 8. Test that the searchBalance method of DBaseManager works
 * as intended. You asked John for the top three customers (those
 * with the highest account balance) in the test database. Their data
 * is provided in DUMMY_HIGH_BALANCE_CUSTOMERS. Their account balance
 * is in each case at least $96,937.69. The searchBalance method should
 * fill the vector of detailed enetries passed by refeence as first
 * argument with entries corresponding to customers with an account
 * balance greater or equal to the value passed as second argument.
 */
TEST_F(DBaseManagerTest, searchBalanceOnTestDBShouldReturnThreeCustomersWithHighestBalance)
{

}

/* Task 9. Test that searchDebtors works as intended. (difficult)
 *
 * The method should fill the vector of tuples (surname, debt) passed
 * by reference as first argument with entries corresponding to clients
 * who owe the bank at least as much as passed as second argument. Debt
 * values are negative numbers.
 *
 * John says that the bank's highest debtors were Mrs. Mickle with a debt
 * of $998.06, Mr. Cadiz with a debt of $997.19 and Mrs. Rothenberger
 * with a debt of $990.44.
 *
 * They were the only customers with a debt greater than $970, so you
 * can use -970.0f as the argument for the tested method.
 *
 * Hint: the difficulty here is tuple element matching in a short,
 * compact and readable way. Consider using the ResultOf matcher.
 * It does not however accept get<0> and get<1> as functions,
 * so yo'd need to make (somewhat tedious) wrappers for them.
 *
 */
TEST_F(DBaseManagerTest, searchDebtorsReturnsGreatestDebtors)
{

    std::vector<std::tuple<std::string, float>> simpleDB;
    constexpr float DUMMY_VALUE1=0;


    sut->searchDebtors(simpleDB,DUMMY_VALUE1);

    for(auto a : simpleDB )
        std::cout<<std::get<0>(a)<<"  "<<std::get<1>(a)<<std::endl;

    ASSERT_THAT(simpleDB, ElementsAre( ResultOf([](std::tuple<std::string, float>  a_rTarget ) {return std::get<1>(a_rTarget);},DUMMY_VALUE1)   ));

}



MOCKS

Tworzenie mocka:
int qux (std::string a_bin)           metoda qux zwraca int a przyjmuje string
MOCK_METHOD N qux int (std::string a_bin)     kolejnosc w kocku (N-liczba zmiennych)
i mock wygląda tak:
MOCK_METHOD1(qux, int(std::string a_bin));


mockowanie nievirtualnych: Napisać mocklasę nie dziedziczącą po oryginalnej) i wstrzykiwać ja do oryginalnego kodu (za pomocą #ifdef UNITTEST )

Google Mock ogarnia przeciążone metody ale uważać z wildcardami __

przyklad 07 z przeciążonym mockiem i wildcardem __:


class BarIf
{
public:
    virtual void registerAddedOne(int & a_rArg) = 0;
    virtual void registerAddedOne(float & a_rArg) = 0;
};


//mock do niej:
#include <gmock/gmock.h>
#include "BarIf.h"

struct GMockBar : public BarIf
{
    MOCK_METHOD1(registerAddedOne, void(int & a_rArg));
    MOCK_METHOD1(registerAddedOne, void(float & a_rArg));
};

kod testu:

#include <gtest/gtest.h>
#include <memory>
#include <iostream>
#include "Foo.h"
#include "GMockBar.h"

//#define StrictMock NaggyMock

namespace
{
    using namespace testing;

    constexpr float DUMMY_F_VALUE_1 = 3.14f;
    constexpr float DUMMY_F_VALUE_2 = 2.72f;

    constexpr FooFloatData DUMMY_F_DATA { DUMMY_F_VALUE_1,
                                          DUMMY_F_VALUE_2 };
}

struct FooTest : public Test
{
    FooTest()
    {
        std::cout << "Constructor!" << std::endl;
    }

    ~FooTest() override
    {
        std::cout << "Destructor!" << std::endl;
    }

    void SetUp() override
    {
        std::cout << "SetUp!" << std::endl;
        barMock = std::make_unique<StrictMock<GMockBar>>();
        sut = std::make_unique<Foo>(barMock.get());
    }

    void TearDown() override
    {
        std::cout << "TearDown!" << std::endl;
    }

    auto matchFooDataFields(const FooData & a_rExpectedFooData)
    {
      return AllOf( Field( &FooData::x, Eq(a_rExpectedFooData.x) ),
                    Field( &FooData::y, Eq(a_rExpectedFooData.y) ) );
    }

    std::unique_ptr<StrictMock<GMockBar>> barMock;
    std::unique_ptr<Foo> sut;
};

TEST_F(FooTest, callingRandomizeShouldRandomizeArgument)
{
    FooFloatData testData = DUMMY_F_DATA;
    sut->randomize(testData);
    EXPECT_NE(testData.x, DUMMY_F_DATA.x);
    EXPECT_NE(testData.y, DUMMY_F_DATA.y);
}

/*

TEST_F(FooTest, callingRandomizeShouldRandomizeArgument2)
{
    FooFloatData testData = DUMMY_F_DATA;
    EXPECT_CALL(*barMock, registerAddedOne(_));
    sut->randomize(testData);
    EXPECT_NE(testData.x, DUMMY_F_DATA.x);
    EXPECT_NE(testData.y, DUMMY_F_DATA.y);
}

*/


TEST_F(FooTest, callingRandomizeShouldRandomizeArgument2)
{
    FooFloatData testData = DUMMY_F_DATA;
    EXPECT_CALL(*barMock, registerAddedOne(An<float&>()));
    sut->randomize(testData);
    EXPECT_NE(testData.x, DUMMY_F_DATA.x);
    EXPECT_NE(testData.y, DUMMY_F_DATA.y);
}


NICE   STRICT    NAGGY 

nice        -  nic nie zgłosi
naggy      - warning
strict        - error i fail testu


upraszczanie interfejsów
(np bardzo dużo argumentów, mock max do 10) napisać wraper na mocka, nadpisana klasa testowa wywołuje nową metodę z małą ilością argumentów (nazwana  ...Simple) która to jest już poprawnie zmockowana:

przykład 08

#define GMOCKBAR_H
#include <gmock/gmock.h>
#include "BarIf.h"

struct GMockBar : public BarIf
{

    
    virtual void qux(int x,
             float y,
             double z,
             std::string s,
             int * l,
             float * m,
             double * n,
             char & a,
             int & b,
             float & c,
             double & d)
    {
        simpleQux(x, s);
    }

    MOCK_METHOD2(simpleQux, void(int x, std::string s));
};

#endif // GMOCKBAR_H



#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <memory>
#include <iostream>
#include "Foo.h"
#include "GMockBar.h"

//#define StrictMock NaggyMock

namespace
{
    using namespace testing;
}

struct FooTest : public Test
{
    FooTest()
    {
    }

    ~FooTest() override
    {
    }

    void SetUp() override
    {
        barMock = std::make_unique<StrictMock<GMockBar>>();
        sut = std::make_unique<Foo>(barMock.get());
    }

    void TearDown() override
    {
    }

    std::unique_ptr<StrictMock<GMockBar>> barMock;
    std::unique_ptr<Foo> sut;
};

TEST_F(FooTest, callingCorgeShouldmakeCallToQux)
{
    EXPECT_CALL(*barMock, simpleQux(Eq(1), StrEq("foo")));
    sut->corge();
}



EXPECT_CALL and ON_CALL

EXPECT_CALL( mockClassObject, method(argMatcher1, argMatcher2, ...) );

jeśli z pointera to *mockClassObject

.Times(1);   jeden raz
EXPECT_CALL( mockClassObject, method(...) ).Times(1);

If you know what you’re doing (do not abuse), you can even (or use ON_CALL)
EXPECT_CALL( mockClassObject, method(...) ).Times(AnyNumber());

Times(0);  - sprawdzanie czy się nie zawoła.

Zwracanie wartości:

WillOnce();   - zwróci raz

We can do that multiple times
EXPECT_CALL( mockClassObject, method(...) ).WillOnce(...).WillOnce(...);
Don’t abuse WillRepeatedly to specify default behavior (use ON_CALLs).
EXPECT_CALL( mockClassObject, method(...) ).WillRepeatedly(...);

musi być określone konkretnie co zwóci:
EXPECT_CALL( mockClassObject, method(...) ).WillOnce(Return(value));


Zwracanie przez referencję:

For this purpose the actions SetArgReferee<n> and SetArgPointee<n>
The n is the argument number (0-based).
We can also invoke functions with Invoke(f)


jeśli kilka rzeczy do zrobienia DoAll( ... )


można zrobić tak, że w SetUp dać ON_CALL na jakiegoś mocka (zawsze można go wywołać) a w konkretnym teście nadpisać go EXPEC_CALLem  (EXPECT_ nadpisuje ON_)


przykład 09:


#include <gtest/gtest.h>
#include <memory>
#include "Foo.h"
#include "GMockBar.h"
#include "GMockBaz.h"

// #define NiceMock StrictMock

namespace
{
    using namespace testing;

    constexpr int DUMMY_SERIAL = 12345;
    constexpr float DUMMY_VALUE = 3.14f;
}

struct FooTest : public Test
{
    void SetUp() override
    {
        barMock = std::make_unique<NiceMock<GMockBar>>();
        bazMock = std::make_unique<NiceMock<GMockBaz>>();

        // ON_CALL(*barMock, registerFoo(An<Foo*>())).WillByDefault(Return());
        ON_CALL(*barMock, getDateCreated(An<Foo*>(), An<std::string&>()))
                .WillByDefault(SetArgReferee<1>("29.02.2021"));

        sut = std::make_unique<Foo>(barMock.get());
    }

    void TearDown() override
    {

    }

    std::unique_ptr<NiceMock<GMockBar>> barMock;
    std::unique_ptr<NiceMock<GMockBaz>> bazMock;
    std::unique_ptr<Foo> sut;
};

TEST_F(FooTest, createFooTest)
{
    std::string date = "30.02.2020";
    std::string empty;

    EXPECT_CALL(*barMock, registerFoo(An<Foo*>()));
    EXPECT_CALL(*barMock, getDateCreated(An<Foo*>(), StrEq(empty)))
                .WillOnce(SetArgReferee<1>(date));

    Foo newFoo(barMock.get());
}

TEST_F(FooTest, corgeShouldUpdateInfoIfWorkingProperly)
{
    EXPECT_CALL(*barMock, getBaz()).WillOnce(Return(bazMock.get()));
    EXPECT_CALL(*bazMock, getData(An<int*>(), FloatEq(0.0f)))
            .WillOnce(DoAll(SetArgPointee<0>(DUMMY_SERIAL),
                            SetArgReferee<1>(DUMMY_VALUE),
                            Return(true)));
    EXPECT_CALL(*barMock, updateInfo(Eq(sut.get()), Eq(DUMMY_SERIAL), Eq(DUMMY_VALUE)));

    sut->corge();
}

TEST_F(FooTest, corgeShouldNotUpdateInfoIfGetDataReturnsFalse)
{
    EXPECT_CALL(*barMock, getBaz()).WillOnce(Return(bazMock.get()));
    EXPECT_CALL(*bazMock, getData(An<int*>(), FloatEq(0.0f)))
            .WillOnce(DoAll(SetArgPointee<0>(DUMMY_SERIAL),
                            SetArgReferee<1>(DUMMY_VALUE),
                            Return(false)));
    EXPECT_CALL(*barMock, updateInfo(_,_,_)).Times(0);

    sut->corge();
}

TEST_F(FooTest, corgeShouldDoNothingIfGetBazReturnsNullptr)
{
    EXPECT_CALL(*barMock, getBaz()).WillOnce(Return(nullptr));
    EXPECT_CALL(*bazMock, getData(_,_)).Times(0);
    EXPECT_CALL(*barMock, updateInfo(_,_,_)).Times(0);

    sut->corge();
}

TEST_F(FooTest, getValueShouldReturnFieldSetByCorge)
{
    EXPECT_CALL(*barMock, getBaz()).WillOnce(Return(bazMock.get()));
    EXPECT_CALL(*bazMock, getData(An<int*>(), FloatEq(0.0f)))
            .WillOnce(DoAll(SetArgPointee<0>(DUMMY_SERIAL),
                            SetArgReferee<1>(DUMMY_VALUE),
                            Return(true)));
    EXPECT_CALL(*barMock, updateInfo(Eq(sut.get()), Eq(DUMMY_SERIAL), Eq(DUMMY_VALUE)));

    sut->corge();

    EXPECT_FLOAT_EQ(sut->getValue(), DUMMY_VALUE);
}

/*


        ON_CALL(*barMock, getBaz()).WillByDefault(Return(bazMock.get()));
        ON_CALL(*bazMock, getData(An<int*>(), FloatEq(0.0f)))
                .WillByDefault(DoAll(SetArgPointee<0>(DUMMY_SERIAL),
                               SetArgReferee<1>(DUMMY_VALUE2),
                               Return(true)));

TEST_F(FooTest, getValueShouldReturnFieldSetByCorge2)
{
    sut->corge();

    EXPECT_FLOAT_EQ(sut->getValue(), DUMMY_VALUE2);
}*/


tu w liniach 56-60 dobry przykład zwracania przez wartość:

    EXPECT_CALL(*bazMock, getData(An<int*>(), FloatEq(0.0f)))
            .WillOnce(DoAll(SetArgPointee<0>(DUMMY_SERIAL),
                                       SetArgReferee<1>(DUMMY_VALUE),
             Return(true)));


Pamiętaj, ustawiaj Expec_Calle w kolejności ich wywoływania!

RetiresOnSaturation
po wielokrotnym wywołaniu znika (idzie na emeryture :-)

Przyklad 10

#include <gtest/gtest.h>
#include <memory>
#include "Foo.h"
#include "GMockBaz.h"
namespace
{
using namespace testing;
}
struct FooTest : public Test
{
void SetUp() override
{
bazMock = std::make_unique<NiceMock<GMockBaz>>();
sut = std::make_unique<Foo>(bazMock.get());
}
void TearDown() override
{
}
std::unique_ptr<NiceMock<GMockBaz>> bazMock;
std::unique_ptr<Foo> sut;
};
/* Requirement: corge should keep calling
* getData until it returns true and data is received.
*
* Test plan: mockup of getData returns false 6 times,
* called 7th time returns true and writes data.
*
*/
TEST_F(FooTest, corgeShouldRepeatedlyAskForDataUntilSuccess)
{
EXPECT_EQ(sut->getData(), -1);
EXPECT_CALL(*bazMock, getData(_))
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(DoAll(SetArgReferee<0>(13),
Return(true)));
/*EXPECT_CALL(*bazMock, getData(_)).WillOnce(
DoAll(SetArgReferee<0>(13),
Return(true)));
EXPECT_CALL(*bazMock, getData(_)).Times(6)
.WillRepeatedly(Return(false));
//.RetiresOnSaturation();*/
sut->corge();
EXPECT_EQ(sut->getData(), 13);
}


Castowanie typu MAtcherów
patrz 3.4.11


MOCK std::function
uzywamy AsStdFunction();  patrz 3.4.12


Zadanie LAB02  (tetris)


/* This is the main part of the excercise, where you will
 * use the mocks you wrote earlier to thest TetrominoWell.
 *
 * The tests you will mostly write, in sequence, correspond to
 * a game of Tetris in a miniature Well where square-shaped
 * Tetrominos keep falling down until they stack and trigger
 * a game-over.
 *
 */
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "TetrisWell.hpp"
#include "TetrominoGeneratorMock.hpp"
#include "TetrominoMock.hpp"
#include <memory>
#include <iostream>

/* The anonymous namespace contains predefined Well states
 * for your convenience. The tasks will guide you which ones to
 * use, but you can check them out now if you wish.
 *
 */
namespace
{
    using namespace ::testing;

    const int BOARD_DIM_X = 6;
    const int BOARD_DIM_Y = 5;
    const std::string EMPTY_BOARD = "┃ ░ ░ ░┃\n"
                                    "┃ ░ ░ ░┃\n"
                                    "┃ ░ ░ ░┃\n"
                                    "┃ ░ ░ ░┃\n"
                                    "┃ ░ ░ ░┃\n"
                                    "┗━━━━━━┛\n";
    const std::string BOARD_SPAWN = "┃ ░ ░ ░┃\n"
                                    "┃ ░" BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR " ░┃\n"
                                    "┃ ░" BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR " ░┃\n"
                                    "┃ ░ ░ ░┃\n"
                                    "┃ ░ ░ ░┃\n"
                                    "┗━━━━━━┛\n";
    const std::string BOARD_TICK1 = "┃ ░ ░ ░┃\n"
                                    "┃ ░ ░ ░┃\n"
                                    "┃ ░" BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR " ░┃\n"
                                    "┃ ░" BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR " ░┃\n"
                                    "┃ ░ ░ ░┃\n"
                                    "┗━━━━━━┛\n";
    const std::string BOARD_TICK2 = "┃ ░ ░ ░┃\n"
                                    "┃ ░ ░ ░┃\n"
                                    "┃ ░ ░ ░┃\n"
                                    "┃ ░" BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR " ░┃\n"
                                    "┃ ░" BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR " ░┃\n"
                                    "┗━━━━━━┛\n";
    const std::string BOARD_SPAWN2 = "┃ ░ ░ ░┃\n"
                                     "┃ ░" BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR " ░┃\n"
                                     "┃ ░" BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR " ░┃\n"
                                     "┃ ░" BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR " ░┃\n"
                                     "┃ ░" BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR " ░┃\n"
                                     "┗━━━━━━┛\n";
    const std::string BOARD_RIGHT_M1 = "┃ ░ ░ ░┃\n"
                                       "┃ ░ ░ ░┃\n"
                                       "┃ ░ " BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR "░┃\n"
                                       "┃ ░ " BEGIN_YELLOW "█" END_COLOR BEGIN_YELLOW "█" END_COLOR "░┃\n"
                                       "┃ ░ ░ ░┃\n"
                                       "┗━━━━━━┛\n";
}

/* Task 7. Write the TetrisWellTest class.
 *
 * Remember to include the following members:
 *   + system under test
 *   + mock of Tetromino
 *   + mock of TetrominoGenerator
 *   + atomic int to use as action parameter
 *   + atomic bool flag signaling game over
 * Use smart pointer wherever applicable.
 *
 * In SetUp, initialize all of the above properly.
 */
struct TetrisWellTest : public Test
{

    void SetUp() override
    {
        actionvar.store(0);
        gameover.store(false);

        TetroGenMock = std::make_shared<NiceMock<TetrominoGeneratorMock>>();
        TetroMock=std::make_shared<NiceMock<TetrominoMock>>();

        sut = std::make_unique<TetrisWell>(BOARD_DIM_X,BOARD_DIM_Y,TetroGenMock,&actionvar, &gameover);



    }

    void TearDown() override
    {

    }

     std::unique_ptr<TetrisWell> sut;
     std::shared_ptr<NiceMock<TetrominoGeneratorMock>> TetroGenMock;
     std::shared_ptr<NiceMock<TetrominoMock>> TetroMock;

    std::atomic <int> actionvar;
    std::atomic <bool> gameover;



};


/* Task 8. Test that upon initialization the Well is empty.
 * Use the EMPTY_BOARD const. Also check that the flag
 * signaling game over is set to false. You should
 * check this flag in all subsequent tests.
 */
TEST_F(TetrisWellTest, uponInitiationWellShouldBeEmpty)
{
     //fields :
    //getImage is empty?
    //gameover  flag is false ?

    EXPECT_THAT(sut->getImage(),EMPTY_BOARD );
    sut->getImage();




}



/* Task 9. Test that the first call to tick() after initialization of
 * the Well spawns a new Tetromino.
 *
 * You should expect a call to getTetromino of the mocked TetrominoGenerator.
 * The call shoudl return a tetromino object, which is mocked as well in
 * this test.
 *
 * Now calling getImage() should return the Well state defined in BOARD_SPAWN.
 * However, it will make several calls to the Tetromino object, here mocked:
 *  + 4 calls to getType
 *  + 16 calls to at with different coordinates
 *
 * Set up expectations to mock a square tetromino, TetrominoType::Q, for which
 * calls to at(x,y) of the 4x4 shape map
 *
 * 0000
 * 0110
 * 0110
 * 0000
 *
 * return true for 1 and false for 0.
 *
 * For convenience, you can print the result of getImage() and the expected
 * BOARD_SPAWN to std::cout. Expect them to be equal.
 */
TEST_F(TetrisWellTest, firstTickAfterInitiationShouldSpawnTetromino)
{

    EXPECT_CALL(*m_ptertominoGeneratorMock, getTetromino())
            .WillOnce(Return(m_ptertominoMock));

    EXPECT_CALL(*m_ptertominoMock,getType())
            .Times(4)
            .WillRepeatedly(Return(TetrominoType::Q));

    EXPECT_CALL(*m_ptertominoMock,at(_,_))
            .Times(12)
            .WillRepeatedly(Return(false));

    EXPECT_CALL(*m_ptertominoMock,at(1,1))
            .WillOnce(Return(true));
    EXPECT_CALL(*m_ptertominoMock,at(2,1))
            .WillOnce(Return(true));
    EXPECT_CALL(*m_ptertominoMock,at(1,2))
            .WillOnce(Return(true));
    EXPECT_CALL(*m_ptertominoMock,at(2,2))
            .WillOnce(Return(true));


    sut->tick();

    std::string res = sut->getImage();
    EXPECT_THAT(res, StrEq(BOARD_SPAWN));


}


TEST_P - parametryczne

przykład konfigurowania  4.4


przykład 4.5.3 ParamTest EX:


#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "Client.h"
#include "Result.h"
//#include "TetrominoGeneratorMock.hpp"
#include "CommunicationAdapterIf.h"
#include "MessageIds.h"

#include <memory>
#include <iostream>

namespace
{
    using namespace ::testing;

}


struct CommunicationMock: public CommunicationAdapterIf
{

    MOCK_METHOD1( send, Result (std::unique_ptr<Signal>) );
};



struct ClientTest : public Test
{

    void SetUp() override
    {


        CommMock = std::make_shared<NiceMock<CommunicationMock>>();


        sut = std::make_unique<Client>(CommMock);



    }

    void TearDown() override
    {

    }

     std::unique_ptr<Client> sut;

     std::shared_ptr<NiceMock<CommunicationMock>> CommMock;






};

/*
enum class MessageId
{
    GET_RESOURCE_DATA,
    SET_CONFIG_DATA,
    DELETE_CONFIG_DATA,
    SUBSCRIBE_REQ
};*/


struct FooParametricTest : public ClientTest,
public WithParamInterface<std::tuple<MessageId, std::string>>
{ };
INSTANTIATE_TEST_CASE_P(FooParametricTestSuite,
FooParametricTest,
Values(std::make_tuple(MessageId::SUBSCRIBE_REQ, "SENS01"),
        std::make_tuple(MessageId::SET_CONFIG_DATA , "SENS02"),
       std::make_tuple(MessageId::GET_RESOURCE_DATA , "SENS03"),
        std::make_tuple(MessageId::DELETE_CONFIG_DATA, "SENS04")),);

TEST_P(FooParametricTest, firsttest)
{

    const auto paramTuple = GetParam();
    const auto intToConvert = std::get<0>(paramTuple);
    const auto expectedResult = std::get<1>(paramTuple);

    EXPECT_CALL(*CommMock,send(Pointee(AllOf(Field(&Signal::messageId, Eq(static_cast<uint32_t>(intToConvert))),

                                             Field(&Signal::sensorName, StrEq(expectedResult))
                                           ))) ); //.WillOnce(Return (expectedResult));

    sut->sendMessage(intToConvert);


}


Przykład zadania TDD wykorzystującego Testy parametryczne LINK






Rady:


 - zamiast friendclass stwórz w siucie testowej klasę dziedziczącą po tej testowanej w niej dopisz metodę dostępową do testowanego obszaru

- SetUp jest miejscem gdzie można uruchamiać EXPECT_CALL, w konstruktorze makro nie zadziała

-nazwy testów bez podkreślników

-testowanie metod prywatnych...
nie stosuj:
#ifdef UNITTEST
friend class FooTest;
#endif
...
a już na pewno NIE ;-)
...
#ifdef UNITTEST
#define private public
#endif

zamiast tego jeśli są virtualne to w c++ można zmienić specyfikator z private na public w klasie testowej. Można również dodać virtual jako rozwiązanie!

-mockowanie nievirtualnych: Napisać mocklasę (nie dziedziczącą po oryginalnej) i wstrzykiwać ja do oryginalnego kodu (za pomocą #ifdef UNITTEST )

- mockowanie zwykłych funkcji nie metod klasy:  make a wrapper class with a
method calling the function (with an interface) and use it instead.

- zaleca się stosować nice mocka by nie utrudniać pracy w przyszłości ale można sprawdzić podczas pisania jak się zachowuje z wyższym poziomem np tak:  //#define NiceMock NaggyMock

błędy:
- uninteresting - zaskoczenie że została metoda wywołana bez expec_call
- unexpected calls - istnieją calle ale nie spełniają warunków
- unmatched  - byłe xpec_call ale nie został wywołany

- do komunikacji między klasamo zawsze powinny być stosowane interfejsy, wtedy latwiej mockowac i testowac

-ustawiaj Expec_Calle w kolejności ich spodziewanego wywoływania!

- co zrobić jak mock jest uniquepointerem (zniknie) - unique mock wrapper jest rozwiązaniem

- od wersji google test 1.8.1 działą R-value czyli możemy przekazywać przez &&


Pliki: