Bryan Fok
Bryan Fok

Reputation: 3487

How to use gmock to mock a template method from a class?

How to use gmock to mock a template method (not a template class) for a class? Example a class like this, I want to mock this class, and this template method..

class A{
public:
  template<EnumType ENUM_VALUE>
  int getType(int val);
};

I know how to mock a class with non-virtual methods, or mock a templated class, but i dont know how to mock a non-templated class with a templated method..

Upvotes: 8

Views: 5213

Answers (2)

Bryan Fok
Bryan Fok

Reputation: 3487

I end up doing a relay to mock the method

E.g.

class MOCK_A{
public:
  template<Enum ENUM_VALUE>
  int getType(int val){
    getType(val, ENUM_VALUE);
  }
  
  MOCK_METHOD1(getType, int(int val, Enum enum_value));
};

Upvotes: 0

PiotrNycz
PiotrNycz

Reputation: 24430

  1. First much better solution is to use the implementation of this function A::getType - maybe it does not have to be mocked? E.g.: if it just returns some value that is set in constructor - then just construct A in the way that is needed in your test case:
class A{
public:
  A(int a) : a(a) {}
  template<typename T>
  int getType(int val)
  {
      return a + val;
  }
private:
  int a;
};

 TEST(...)
 {
      A a(TYPE_VALUE_FOR_TEST);
      ...
 }
  1. If it cannot be done that way - then you might consider to have some instrumentation for UT that is switched with preprocessor macros:
 #ifdef TESTING
 namespace Testing
 {
 using std::pair<std::type_index, int> AGetTypeKey;
 std::map<AGetTypeKey, int> AGetTypeExpectedValues;
 template <typename T>
 void expectAGetType(int inputValue, int expectedResult)
 {
      AGetTypeExpectedValues[AGetTypeKey(std::type_index(typeid(T)), inputValue)] = expectedResult;
 }
 template <typename T>
 int getAGetType(int value)
 {
      return AGetTypeExpectedValues[AGetTypeKey(std::type_index(typeid(T)), inputValue)];
 }
 }
 #endif

class A{
public:
  A(int a) : a(a) {}
  template<typename T>
  int getType(int val)
  {
  #if TESTING
      return Testing::getAGetType<T>(val);
  #else
      // your "normal" implementation
      ...
  #endif
  }
private:
  int a;
};

 // compiled with -DTESTING=1
 #ifndef TESTING
 #error ...
 #endif
 TEST(...)
 {
      Testing::expectAGetType<float>(EXPECTED_INPUT_VALUE,
                                     TYPE_VALUE_FOR_FLOAT);
      ...
 }

Regarding point-2 - of course all testing code should be carefully separated from "normal code" - e.g. in some separated header files.

It is worth to say that none of these solution is perfect - and this second solution might not be 100% reliable as you would test not real code but some testable version of it.

Maybe you should start from rethinking your design - as it seems the design was not completed with "design for testability" in mind.

Upvotes: 2

Related Questions