Pokazywanie postów oznaczonych etykietą GoogleMock. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą GoogleMock. Pokaż wszystkie posty

29 października 2012

StrictMock i referencja w konstruktorze [TRICK]

Napotkałem ostatnio taki problem (mowa tu o testach charakteryzacyjnych).

Przykład: Chcemy przetestować metodę Filter::run(). Klasa do działania potrzebuje obiektów Inc i Show. Nie mają one czysto abstrakcyjnych interfejsów (ale zrobimy dla nich mocki). Show potrzebuje do działania referencji do Inc.

Problem pojawia się, gdy mock zrobimy StrictMock-iem i będziemy chcieli go jeszcze zainicjować. Tutaj ShowMock chcemy zainicjować IncMock (linijka 49-50).
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/ref.hpp>
#include <gmock/gmock.h>

using namespace std;
using namespace ::testing;

class Inc {
public:
    virtual int inc(int v) { return v++; }
};

class IncMock : public Inc {
public:
    MOCK_METHOD1(inc, int(int));
};

class Show {
    Inc& obj;
public:
    Show(Inc& i) : obj(i) {}
    virtual void show(int v) { cout << "Val: " << obj.inc(v) << endl; }
};

class ShowMock : public Show {
public:
    ShowMock(Inc& i) : Show(i) {}
    MOCK_METHOD1(show, void(int));
};

class Filter {
    boost::shared_ptr<Show> sw;
    boost::shared_ptr<Inc> in;
public:
    Filter(boost::shared_ptr<Show> s, 
           boost::shared_ptr<Inc> i) : sw(s), in(i) {}
    void run(int val) {
        if (val > 5)
            sw->show(val);
        in->inc(val);
    }
};

TEST(FilterTest, test) {
    boost::shared_ptr<IncMock> l_incMock(new StrictMock<IncMock>());

//  NOT WORKING
//  boost::shared_ptr<ShowMock> l_showMock(new StrictMock<ShowMock>
//                                           (*l_incMock));
//  NOT WORKING
//  boost::shared_ptr<ShowMock> l_showMock(new StrictMock<ShowMock>
//                                           (boost::cref(*l_incMock)));

    boost::shared_ptr<ShowMock> l_showMock(new StrictMock<ShowMock>
                                             (boost::ref(*l_incMock)));
    Filter l_filter(l_showMock, l_incMock);

    int l_val = 8;
    EXPECT_CALL(*l_incMock, inc(l_val)).WillOnce(Return(l_val + 1));
    EXPECT_CALL(*l_showMock, show(l_val));

    l_filter.run(l_val);
}

int main(int argc, char *argv[]) {
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}
Pierwsze rozwiązanie wypluje error przy kompilacji
In file included from /home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock.h:64:0,
                 from main.cpp:4:
/home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock-generated-nice-strict.h: In constructor ‘testing::StrictMock<M>::StrictMock(const A1&) [with A1 = IncMock, MockClass = ShowMock]’:
main.cpp:48:77:   instantiated from here
/home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock-generated-nice-strict.h:174:51: error: no matching function for call to ‘ShowMock::ShowMock(const IncMock&)’
/home/niegodziwy/Downloads/gmock-1.6.0/include/gmock/gmock-generated-nice-strict.h:174:51: note: candidates are:
main.cpp:28:5: note: ShowMock::ShowMock(Inc&)
main.cpp:28:5: note:   no known conversion for argument 1 from ‘const IncMock’ to ‘Inc&’
main.cpp:26:7: note: ShowMock::ShowMock(const ShowMock&)
main.cpp:26:7: note:   no known conversion for argument 1 from ‘const IncMock’ to ‘const ShowMock&’
Co się okazuje, StrictMock ma tylko konstruktory explicit, dlatego podziała trick z boost::ref().
template <typename A1>
explicit StrictMock(const A1& a1) : MockClass(a1) {
  ::testing::Mock::FailUninterestingCalls(
      internal::ImplicitCast_<MockClass*>(this));
}
Niestety nie wiem dlaczego nie działa boost::cref() ?!

12 października 2012

SetArgReferee w DoAll

Czasami kod z którym mamy do czynienia, przekazuje nam wynik swojego działa zapisując go do argumentu przekazanego przez referencję. Chciało by się żeby wyglądało to troszkę schludniej, jednakże z powodów optymalizacyjnych (C++11 wprowadza coś takiego jak move constructor), trzeba z tym żyć, no i czasami testować. Google Mock wychodzi na przeciw, dostarczając m.in. metodę SetArgReferee() (jest też wersja dla wskaźników).

Poniżej będziemy testować klasę Simple, która posiada jedną metodą do zwracania pierwszego elementu wektora - returnFirst().

Przez "dependency injection", do klasy wstrzykiwany jest obiekt, który zadziała na wektorze (obróci go) nim zostanie zwrócony ten pierwszy (czyli ostatni element). Ten obiekt sobie z-mockujemy i tutaj przyda nam się SetArgReferee.
class IVecReverse {
public:
    virtual bool myReverse(std::vector<int>& v) const = 0;
};

class VecReverse : public IVecReverse {
public:
    bool myReverse(std::vector<int>& v) const {
        if (not v.empty()) {
            std::reverse(v.begin(), v.end());
            return true;
        }
        return false;
    }
};

class Simple {
public:
    Simple(IVecReverse& vs) : VecReverse(vs) {}

    int returnFirst(std::vector<int>& v) {
        if (VecReverse.myReverse(v))
            return v[0];
        return -1;
    }
protected:
    IVecReverse& VecReverse;
};
A teraz test. A więc z-mockowana metoda myReverse(), powinna przyjąć jakiś wektor (v1), który na końcu ma być odwrócony (to będzie nasze v2). Całą tą akcję trzeba zamknąć w DoAll(). W nawiasach <> dla SetArgReferee, wskazujemy numer parametru, dla którego robimy podmianę (mamy jeden argument więc będzie zero).
#include <iostream>
#include <algorithm>
#include <vector>
#include <boost/assign/std/vector.hpp>
#include <boost/assign/list_of.hpp>
#include <gtest/gtest.h>
#include <gmock/gmock.h>

using namespace boost::assign;
using namespace ::testing;

class VecReverseMock : public IVecReverse {
public:
    MOCK_CONST_METHOD1(myReverse, bool (std::vector<int>&));
};

class SimpleTest : public Test {
public:
    VecReverseMock vrMock;
};

TEST_F(SimpleTest, testReturnFirst)
{
    Simple simple(vrMock);
    std::vector<int> v1 = boost::assign::list_of(3)(2)(4);
    std::vector<int> v2 = boost::assign::list_of(4)(2)(3);

    // Return musi byc ostatnie
    EXPECT_CALL(vrMock, myReverse(v1))
            .WillOnce(DoAll(SetArgReferee<0>(v2), Return(true)));
    EXPECT_EQ(4, simple.returnFirst(v1));
}

int main(int argc, char *argv[])
{
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}
A teraz krótko o DoAll(a1, a2, ..., an) - jego zadaniem jest wykonanie wszystkich akcji od "a1" do "an", wszystkie poza "an" powinny zwracać void, "an" musi coś zwrócić bo taki będzie wynik całego DoAll (w naszym przypadku jest to Return(true)).
[----------] 1 test from SimpleTest
[ RUN      ] SimpleTest.testReturnFirst
[       OK ] SimpleTest.testReturnFirst (1 ms)
[----------] 1 test from SimpleTest (1 ms total)

28 września 2012

GoogleTest/Mock instalacja

Piekło zamarzło, nie udało mi się zainstalować narzędzi "Google Mock" i "Google Test" z repozytorium Ubuntu ("google-mock", "libgtest-dev"). Tzn. udało się, ale nijak nie zrozumiałem jak z linkować to co dostałem z moim projektem/testem. Według nich powinienem zadziałać skryptem (gtest-config), tylko gdzie go można znaleźć...?
g++ $(gtest-config --cppflags --cxxflags) -o foo.o -c foo.cpp
g++ $(gtest-config --ldflags --libs) -o foo foo.o
W ramach kompilacji chciałem przeprowadzić standardowe budowanie ze skryptów make. Wpierw okazało się, że autoconf/make(?) nie jest już wspierany (albo odradzany), i że powianiem skorzystać z CMake. Tak też uczyniłem, lecz to co zostało wyplute, nijak nie chciało współgrać z linkerem.

Skończyło się ręcznym kompilowaniu bibliotek. Wpierw ustawiłem sobie kilka zmiennych środowiskowych, żeby było bardziej przejrzyście.
export GMOCK_DIR=~/Downloads/gmock-1.6.0/
export GTEST_DIR=~/Downloads/gmock-1.6.0/gtest/
export GMOCK_INCLUDE=~/Downloads/gmock-1.6.0/include/
export GTEST_INCLUDE=~/Downloads/gmock-1.6.0/gtest/include/
export GMOCK_LIB=~/Downloads/gmock-1.6.0
Kompilacja do statycznych bibliotek
g++ -I${GTEST_INCLUDE} -I${GTEST_DIR} -I${GMOCK_INCLUDE} \
    -I${GMOCK_DIR} -c ${GTEST_DIR}/src/gtest-all.cc
g++ -I${GTEST_INCLUDE} -I${GTEST_DIR} -I${GMOCK_INCLUDE} \
    -I${GMOCK_DIR} -c ${GMOCK_DIR}/src/gmock-all.cc
ar -rv libgmock.a gtest-all.o gmock-all.o
Prosty test:
#include <gtest/gtest.h>

using namespace testing;

int main()
{
    EXPECT_EQ(1, 2);
    return 0;
}
I wreszcie kompilacja testu:
g++ -I${GTEST_INCLUDE} -I${GMOCK_INCLUDE} \
    main.cpp ${GMOCK_LIB}/libgmock.a -lpthread -o main
Oraz wynik:
$ ./main 
main.cpp:7: Failure
Value of: 2
Expected: 1