dimanche 3 janvier 2021

Using GoogleMock to avoid dependencies when testing a Card and a CardCollection class

I'm learning about mocking and am reasonably new to the concepts. For the sake of learning, I'm implementing a Card class and a CardCollection class which depends on the Card. Therefore I've created a MockCard for testing the CardCollection.

Here's the Card.h

    class ICard {
    protected:
        int rank_;
        std::string suit_;
    public:

        ICard(int rank, std::string suit) : rank_(rank), suit_(std::move(suit)) {};

        [[nodiscard]] virtual int getRank() const = 0;

        [[nodiscard]] virtual std::string getSuit() const  = 0;

        friend std::ostream &operator<<(std::ostream &os, const ICard &c);

        bool operator>(const ICard &other) const;

        bool operator<(const ICard &other) const;

        bool operator<=(const ICard &other) const;

        bool operator>=(const ICard &other) const;

        bool operator==(const ICard &other) const;

        bool operator!=(const ICard &other) const;
    };

    class Card : public ICard {
    public:

        Card(int r, std::string s);

        [[nodiscard]] int getRank() const override;

        [[nodiscard]] std::string getSuit() const override;

    };

The MockCard.h

#include "gmock/gmock.h"
class MockCard : public ICard {
public:
    MockCard(int rank, std::string suit) : ICard(rank, std::move(suit)){};
    MOCK_METHOD(int, getRank, (), (const, override));
    MOCK_METHOD(std::string, getSuit, (), (const, override));
};

And CardCollection.h:

    #include "Card.h"
    class CardCollection {

    protected:
        vector<ICard *> cards_;
    public:

        CardCollection() = default;

        ~CardCollection() = default;

        explicit CardCollection(vector<ICard*> cards);

        ICard* pop();

    ...

Here is my test:

#include "gmock/gmock.h"
TEST(CardCollectionTests, TestPop) {
    MockCard card1(6, "C");
    std::vector<ICard*> cards({&card1});
    CardCollection cc(cards);

    // pop the card off the collection, cast to MockCard
    auto* new_card = (MockCard*) cc.pop();

    ASSERT_EQ(6, new_card->getRank());
}

The best test that I can think of is to verify that the Card is a 6 of clubs. But we can't use the Card class directly as this would introduce unwanted dependencies into the unit test. So I resolve to cast the ICard* returned from CardCollection::pop to a MockCard* type so that we can use the getRank() method and test that the value returned is a 6. However, this is a MockCard not a Card so getRank() actually returns a 0 and the test fails.

How can I set the value returned by MockCard::getRank() to 6? More generally, Are there any obvious pitfalls you can see in my approach to testing this behavior and if so, what would you implement as an alternative?

Aucun commentaire:

Enregistrer un commentaire