30

I am trying to mock a templated method.

Here is the class containing the method to mock :

class myClass
{
public:
    virtual ~myClass() {}

    template<typename T>
    void myMethod(T param);
}

How can I mock the method myMethod using Google Mock?

HaveNoDisplayName
  • 8,291
  • 106
  • 37
  • 47
Xavier V.
  • 6,068
  • 6
  • 30
  • 35

3 Answers3

14

In previous version of Google Mock you can only mock virtual functions, see the documentation in the project's page.

More recent versions allowed to mock non-virtual methods, using what they call hi-perf dependency injection.

As user @congusbongus states in the comment below this answer:

Google Mock relies on adding member variables to support method mocking, and since you can't create template member variables, it's impossible to mock template functions

A workaround, by Michael Harrington in the googlegroups link from the comments, is to make specialized the template methods that will call a normal function that can be mocked. It doesn't solve the general case but it will work for testing.

struct Foo
{
    MOCK_METHOD1(GetValueString, void(std::string& value));

    template <typename ValueType>
    void GetValue(ValueType& value); 

    template <>
    void GetValue(std::string& value) {
        GetValueString(value);
    } 
};
Ismael
  • 2,995
  • 29
  • 45
  • 1
    Yes, you are right Ismael, for more information, I found this link : http://groups.google.com/group/googlemock/browse_thread/thread/e52b27b7d9b20145 So, there is a workaround to mock templated methods. – Xavier V. Aug 06 '10 at 21:03
  • 1
    To elaborate on this, Google Mock relies on adding member variables to support method mocking, and since you can't create template member variables, it's impossible to mock template functions. – congusbongus Oct 14 '13 at 23:20
  • So we add 'MOCK_METHOD1' in product code? is Foo a mock class or the class being mocked? – SumitV Mar 24 '21 at 04:53
  • @SumitV Foo is a mock class. I wont recommend using mocks in production code. I suggest to read googletest's cookbook https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md. The idea is well explained there. – Ismael Mar 24 '21 at 16:22
1

Here is the original post again with comments to aid in understanding:

    struct Foo 
    { 
        // Our own mocked method that the templated call will end up calling.
        MOCK_METHOD3(GetNextValueStdString, void(const std::string& name, std::string& value, const unsigned int streamIndex)); 

        // If we see any calls with these two parameter list types throw and error as its unexpected in the unit under test.
        template< typename ValueType > 
        void GetNextValue( const std::string& name, ValueType& value, const unsigned int streamIndex ) 
        { 
            throw "Unexpected call."; 
        } 
        template< typename ValueType > 
        void GetNextValue( const std::string& name, ValueType& value ) 
        { 
            throw "Unexpected call."; 
        } 

        // These are the only two templated calls expected, notice the difference in the method parameter list. Anything outside
        // of these two flavors is considerd an error.
        template<> 
        void GetNextValue< std::string >( const std::string& name, std::string& value, const unsigned int streamIndex ) 
        { 
            GetNextValueStdString( name, value, streamIndex ); 
        } 
        template<> 
        void GetNextValue< std::string >( const std::string& name, std::string& value ) 
        { 
            GetNextValue< std::string >( name, value, 0 ); 
        } 
    }; 
Chris
  • 41
  • 6
  • Asked by stackoverflow to review your answer as a new contributor. The only thing I see is your answer needs to stand on its own. You can mention Ismael's answer in your answer, but you shouldn't directly address Ismael, you should address the question. Consider editing it so your answer is complete without reference to the answers of others (although you are still free to reference other answers also). This improves the quality of the answer for users reading it in the future. – JoseOrtiz3 Jan 17 '19 at 22:46
0

Here a solution that doesn't require to implement each template instantiation manually, this is especially helpful if there are a lot of different template instantiations.


struct Foo
{
  template <typename T>
  struct FooImpl
  {
    MOCK_METHOD(void, myMethod, (const T& value), ());
  };

  template <typename T>
  void myMethod(const T& value)
  {
    GetMock<T>()->myMethod(value);
  }

  template <typename T>
  std::shared_ptr<FooImpl<T>> GetMock()
  {
    std::shared_ptr<FooImpl<T>> result;
    std::shared_ptr<void> voidValue;
    if (!(voidValue = mCachedImpls[typeid(T).name()])
    {
      result = std::make_shared<FooImpl<T>>();
      mCachedImpls[typeid(T).name()] = std::reinterpret_pointer_cast<void>(result);
    }
    else
    {
      result = std::reinterpret_pointer_cast<FooImpl<T>>(voidValue);
    }

    return result;
  }

private:
  std::unordered_map<std::string, std::shared_ptr<void>> mCachedImpls;
};

Mock can then be prepared like this:

Foo foo;
EXPECT_CALL(*(foo.GetMock<int>()), myMethod(_)).Times(1);
Martze
  • 921
  • 13
  • 32