Как я могу сопоставить один элемент из кортежа C++ с помощью gmock?


Как я могу сопоставить один элемент из кортежа C++ с помощью gmock ?

Для примера попробуем извлечь std::string из std::tuple<std::string, int>.

Я знаю, что мог бы написать пользовательский сопоставитель следующим образом:

MATCHER_P(match0thOfTuple, expected, "") { return (std::get<0>(arg) == expected); }
Но так как я нашел Pair(m1, m2) совпадение для std::pair, я ожидал также найти что-то подобное для std::tuple.

Gmock имеет Args<N1, N2, ..., Nk>(m) для выбора подмножества аргументов кортежа. При использовании его только с 1 аргументом, он по-прежнему ожидает совпадения кортежей. Следующая попытка делает не похоже, чтобы компилировать:

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

И делает мой лязг дать ошибку компиляции, как это:

.../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; }
...

Существует ли решение gmock для std::tuple, аналогичное решению для std::pair, которое использует сопоставитель gmock Pair?

1 3

1 ответ:

testing::Args это для упаковки аргументов функции в кортеж-совершенно противоположно тому, чего вы хотите добиться.

Мой совет - в вашем случае-распакуйте вещи в макете класса, см.:

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&));
};

Затем:

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

К сожалению, ни один из текущих сопоставителей gmock не работает для Аргументов std::tuple.

------------------------

Если вы хотите узнать о шаблоне C++ - вы можете попробовать это (не полный - просто идея, как это может быть достигнуто, чтобы сделать общую функцию для кортежа соответствие):

// 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);
}

Тогда используйте так:

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