0

Edit: I have classes that receives messages:

class Foo {
public:
    void receive(FooMessage& message));
    // other messages
}
class Bar {
public:
    void receive(BarMessage& message));
    // other messages
}

and mock that test receiving those messages:

class Mock {
public:
    MOCK_METHOD(void, receive, (FooMessage& arg));
    MOCK_METHOD(void, receive, (BarMessage& arg));
    // a lot other messages
}

I wanted to turn Mock into template, so instead of writing long list of MOCK_METHOD() in Mock I would create mock like this:

Mock<BarMessage,BazMessage> myMock;

and have those mock methods generated for me. Is it possible to achieve?


Original question: I have mock class that looks like this:

class foo
{
public:
    MOCK_METHOD(void, foo, (bar& arg));
    MOCK_METHOD(void, foo, (bar& arg));
    // other overloads
}

Is it possible to create those mock methods using template parameter pack? I'm looking to do something like this:

template <typename...Ts>
class foo
{
public:
    template <Ts...t>
    MOCK_METHOD(void, foo, (t& arg))...;
}
273K
  • 29,503
  • 10
  • 41
  • 64
WBurzynski
  • 21
  • 3
  • references to `bar` and `baz` – WBurzynski Jul 30 '21 at 09:08
  • MOCK_METHOD is a complex macro, I think your approach is not practical. We may consider to define macros to call MOCK_METHOD – prehistoricpenguin Jul 30 '21 at 09:15
  • @JeJo I edited question, hopefully now it's clear. – WBurzynski Jul 30 '21 at 09:48
  • @WBurzynski Added an example of Boost Preprocessor for your particular example. Not sure if you have seen that before accepting my answer! – 2b-t Jul 30 '21 at 12:13
  • The only workaround I can see to the Boost.PP answer is to declare a single `MOCK_METHOD(void, foo, std::variant)` and see if that works. I'm not offering it as an answer, because I haven't tested whether it actually behaves - but it's worth a try if you have C++17 (or access to a Boost, experimental or TR variant) – Useless Jul 30 '21 at 12:27
  • @Useless This works only partially. Calls to mock method are registered as expected, but unfortunately matchers don't work. Maybe with some clever conversion or custom matchers i could make this work. – WBurzynski Aug 02 '21 at 09:37

2 Answers2

1

No, this is not possible, at least not in the way sketched out by you above with variadic templates. gMock is based on macros which in C++ are expanded by the pre-processor before compilation, so before variadic macros are considered.

The only chance you have of achieving something like this is by writing a macro of your own similar to this which iterates over the elements of a variadic macro or using the Boost Preprocessor library that contains powerful macros such as BOOST_PP_SEQ_FOR_EACH. With the latter you could define a macro with arguments

#define RECEIVE_MOCK_METHOD(R, DATA, ELEM) MOCK_METHOD(void, receive, (ELEM& arg));

and then call it with Boost Preprocessor like

BOOST_PP_SEQ_FOR_EACH(RECEIVE_MOCK_METHOD, , (FooMessage) (BarMessage) (BazMessage))

resulting in three mock methods

MOCK_METHOD(void, receive, (FooMessage& arg));
MOCK_METHOD(void, receive, (BarMessage& arg));
MOCK_METHOD(void, receive, (BazMessage& arg));

Try it here!

2b-t
  • 2,414
  • 1
  • 10
  • 18
  • Hello, I previously accepted your answer without checking if it works, as i did not have access to boost. Now that i do, i could not make your solution work. Compiler constantly complained about redeclaration of gmock internals. I posted my solution in separate answer. – WBurzynski Nov 22 '22 at 11:56
  • Hi @WBurzynski, indeed you seem to be right! Back then I did verified with GMock itself and thought my code above would be sufficient. This problem seems to occur if you use a macro to generate multiple overloads. GMock internally seems to do some name mangling and creates implementation structs `FunctionMocker` such as `gmock01_foo_30` (where `foo` corresponds to the function name). In case of creating them manually the counter advances (creating e.g. `gmock01_foo_30` and `gmock01_foo_31`) but with the macro I used for some reason the counter does not advance and the compilation fails. – 2b-t Nov 23 '22 at 20:45
  • In case the function name would change (and not only the argument type) this would not be an issue. I tried to have a look at the internals of GMock to see if this could be easily resolved but GMock is quite a complex beast relying internally mostly on macros similar to `BOOST_PP_SEQ_FOR_EACH`. I am not sure if this can be resolved easily. Your work-around should work as long as the different methods do not have to modify and read internal variables of the class or something similar. – 2b-t Nov 23 '22 at 21:11
0

I managed to solve the problem by moving MOCK_METHODS to separate classes and using std::tuple in original class:

template<typename MessageType>
class SingleMessageTypeReceiver
{
public:
    MOCK_METHOD(void, processMessage, (MessageType & e));
    void subscribe(std::shared_ptr<MessageQueue> queue)
    {
        queue->template subscribeMessage<MessageType>(*this);
    }
};

template<typename... MessageTypes> class MockMessageReceiver
{
public:
    template<typename MessageType>
    SingleMessageTypeReceiver<MessageType>& of()
    {
        return std::get<testutil::SingleMessageTypeReceiver<MessageType>>(receivers);
    }

private:
    std::tuple<SingleMessageTypeReceiver<MessageTypes>...> receivers;
};
WBurzynski
  • 21
  • 3