Search code examples
c++googletestgooglemock

How can I match one element from a C++ tuple using gmock?


How can I match one element from a C++ tuple using gmock ?

For example let's try to extract the std::string from a std::tuple<std::string, int>.

I know I could write a custom matcher like this:

MATCHER_P(match0thOfTuple, expected, "") { return (std::get<0>(arg) == expected); }

But since I found the Pair(m1, m2) matcher for the std::pair, I expected also to find something similar for the std::tuple.

Gmock has Args<N1, N2, ..., Nk>(m) for selecting a subset of tuple arguments. When using it with just 1 argument, it still expects a tuple matcher. The following attempt does not seem to compile:

struct {
  MOCK_METHOD1(mockedFunction, void(std::tuple<std::string, int>&));
} mock;
EXPECT_CALL(mock, mockedFunction(testing::Args<0>(testing::Eq(expectedStringValue))));

And makes my clang give a compilation error like this:

.../gtest/googlemock/include/gmock/gmock-matchers.h:204:60: error: invalid operands to binary expression ('const std::__1::tuple<std::__1::basic_string<char> >' and 'const std::__1::basic_string<char>')
  bool operator()(const A& a, const B& b) const { return a == b; }
...

Is there a gmock solution for the std::tuple similar to the one for the std::pair, which uses the gmock Pair matcher?


Solution

  • testing::Args is for packing function arguments to tuple - quite opposite to what you want to achieve.

    My advice - in your case - unpack in Mock class, see:

    struct mock 
    {
      void mockedFunction(std::tuple<std::string, int>& tt)
      {
          mockedFunctionUnpacked(std::get<0>(tt), std::get<1>(tt));
      }
      MOCK_METHOD2(mockedFunctionUnpacked, void(std::string&, int&));
    };
    

    Then:

    EXPECT_CALL(mock, mockedFunctionUnpacked(expectedStringValue, ::testing::_));
    

    Unfortunately none of current gmock matchers works for std::tuple arguments.



    If you like to get learn about C++ template - you might try this (not complete - just an idea how it can be achieved to make general function for tuple matching):

    // Needed to use ::testing::Property - no other way to access one 
    // tuple element as "member function"
    template <typename Tuple>
    struct TupleView
    {
    public:
        TupleView(Tuple const& tuple) : tuple(tuple) {}
        template <std::size_t I>
        const typename std::tuple_element<I, Tuple>::type& get() const
        {
            return std::get<I>(tuple);
        }
    private:
        Tuple const& tuple;
    };
    
    // matcher for TupleView as defined above
    template <typename Tuple, typename ...M, std::size_t ...I>
    auto matchTupleView(M ...m, std::index_sequence<I...>)
    {
        namespace tst = ::testing;
        using TV = TupleView<Tuple>;
        return tst::AllOf(tst::Property(&TV::template get<I>, m)...);
    }
    
    // Matcher to Tuple - big disadvantage - requires to provide tuple type:
    template <typename Tuple, typename ...M>
    auto matchTupleElements(M ...m)
    {
        auto mtv = matchTupleView<Tuple, M...>(m..., std::make_index_sequence<sizeof...(M)>{});
        return ::testing::MatcherCast<TupleView<Tuple>>(mtv);
    }
    

    Then use like this:

    EXPECT_CALL(mock, mockedFunction(matchTupleElements<std::tuple<std::string, int>>(expectedStringValue, ::testing::_)));