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)

Brak komentarzy:

Prześlij komentarz