Search code examples
googlemock

Set expectation for input value for overloaded function where value does not matter


I have a class MyClass which I want to mock in 2 parameterized tests where I have 2 operator *= methods: 1 for an MyClass object and one for a double.

The class looks as follows:

Header:

class MyClass {
private:
    int value;
public:
    MyClass(int _value=0);
    ~MyClass();
    int operator *=(const MyClass &other);
    int operator *=(const double other);

Implementation:

MyClass::MyClass(int _value){
    value = _value;
}

MyClass::~MyClass(){}

int MyClass::operator*=(const MyClass &other){
    value *= other.value;
}

int MyClass::operator*=(const double other){
    value *= double;
}

Now I want to mock this in 2 parameterized tests so I can give any input to the mock and check if the correct overloaded mock-method is called. But I don't care what the exact input values are for the method calls, only that the correct method is called.

So basically I want to combine the following EXPECT_CALLs since the first is ambiguous since there are 2 implementations:

EXPECT_CALL(mymock, multiplication_assignment_operator(_))
    .Times(1)
    .WillOnce(Return(30));

with:

// The ??? in the following line is the question I have.
EXPECT_CALL(mymock, multiplication_assignment_operator(Matcher<MyClass&>(???)))
    .Times(1)
    .WillOnce(Return(30));
// Note: For the double implementation this is Matcher<double>(???).

But I can't find a way to define a Matcher for a type where it only matches the type and skips the value like '_' does.

I already googled around but did not find a solution, hence my question here.

/EDIT/ Added minimal reproducible example as requested by 273K. Probably contains lots of details which I assumed were not directly needed for the question hence I did not listed these implementations before. It can be that I now copied / pasted / anonymized (I'm not allowed to show the actual code, so have to make it generic) too much ;-)

/EDIT 2/ We use gmock 1.10 (yes I know old, but that is the version the company I'm working for is using).

Mock for MyClass - Header:

#ifndef MOCK_MYCLASS_MOCK_CXX_HPP
#define MOCK_MYCLASS_MOCK_CXX_HPP

#include "gmock/gmock.h"
#include "MyClass.h"

namespace MyNameSpace
{
    class MyClass_MockImplNotSetException : publ std::exception
    {
        public:
            virtual const char* what() const throw();
    };

    class IMockMyClass
    {
        public:
            virtual ~IMockMyClass();
            static IMockMyClass* getTestDouble();
            virtual int operator*=(const MyNamespace::MyClass & other) const = 0;
            virtual int operator*=(const double other) = 0;
    };

    class InternalMockIMockMyClass : public IMockMyClass
    {
        protected:
            InternalMockIMockMyClass();

        public:
            virtual ~InternalMockIMockMyClass();
            static IMockMyClass* getTestDouble();
            MOCK_METHOD(int, multiplication_assignment_operator, (const MyNameSpace::MyClass &));
            MOCK_METHOD(int, multiplication_assignment_operator, (const double));
            virtual int operator*=(const MyNameSpace::MyClass & other) { return multiplication_assignment_operator(other); }
            virtual int operator*=(const double other) { return multiplication_assignment_operator(other); }
};

typedef ::testing::NiceMock<InternalMockIMockMyClass> MockMyClass;
typedef ::testing::StrictMock<InternalMockIMockMyClass> StrictMockMyClass;

#endif // MOCK_MYCLASS_MOCK_CXX_HPP

Mock for MyClass - CPP file:

#include "MyClass_mock.hpp"
#include <iostream>

namespace MyNameSpace
{
    const char* MyClass_MockImplNotSetException::what() const throw()
    {
       return "No mock implementation is set to handle MyClass functions";
    }

    static InternalMockIMockMyClass *p_IMockMyClass = nullptr;

    IMockMyClass::~IMockMyClass()
    {
    }

    InternalMockIMockMyClass::InternalMockMyClass()
    {
        p_IMockMyClass = this;
    }

    InternalMockIMockMyClass::~InternalMockIMockMyClass()
    {
        p_IMockMyClass = nullptr;
    }

    IMockMyClass* InternalMockIMockMyClass::getTestDouble()
    {
        IMockMyClass* pSelected = p_IMockMyClass;

        if (p_IMockMyClass == NULL)
        {
            std::cerr << "ERROR: No mock implementation is currently available to handle "
                      << "calls to MyClass functions. Did you forget to construct "
                      << "MockMyClass mock in your test?" << std::endl;
            throw MyClass_MockImplNotSetException();
        }
        return pSelected;
    }

    MyClass::MyClass(int_value)
    {
    }

    MyClass::~MyClass()
    {
    }

    int MyClass::operator*=(const MyNameSpace::MyClass & other)
    {
        int operator_result = InternalMockIMockMyClass::getTestDouble()->operator*=(other);

        return operator_result;

    }

    int MyClass::operator*=(const double other)
    {
        int operator_result = InternalMockIMockMyClass::getTestDouble()->operator*=(other);

        return operator_result;
    }
}

And a simplified test SUT (system under test):

Header:

#ifndef SUT_MY_CLASS_H
#define SUT_MY_CLASS_H

#include "MyClass.hpp"

class SUT_MyClass {
public:
    SUT_MyClass(const MyNameSpace::MyClass &_val);

    ~SUT_MyClass();

    int multiply_and_assignment_values(const SUT_MyClass &other);

    int multiply_and_assignment_values_other_type(const double other);

private:
    MyNameSpace::MyClass val;
};

#endif

Implementation:

#include "SUT_MyClass.hpp"


SUT_MyClass::SUT_MyClass(const MyNameSpace::MyClass &_val){
    val = _val;
}

SUT_MyClass::~SUT_MyClass(){
}

int SUT_MyClass::multiply_and_assignment_values(const SUT_MyClass &other) {
    return val *= other.val;
}

int SUT_MyClass::multiply_and_assignment_values_other_type(const double other) {
    return val *= other;
}

And finally unparameterized tests , which I want to transform in parameterized tests (I did not strip the unneeded gmock using statements):

#include "gtest/gtest.h"
#include "gmock/gmock.h"

/* Mock includes */
#include "MyClass_mock.hpp"

/* SUT include */
#include "SUT_MyClass.hpp"

#include "iostream"

#include <sstream>

/* gtest/mock using statements. Refer to gtest/gmock documentation for more info */
using ::testing::Test;
using ::testing::_;
using ::testing::DoAll;
using ::testing::Eq;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::ReturnRef;
using ::testing::Matcher;
using ::testing::Ref;

class Test_SUT_MyClass : public ::testing::Test
{
protected:
   MyNameSpace::MockMyClass mockMyClass;

   virtual void SetUp()
   {
   }

   virtual void TearDown()
   {
   }
};

TEST_F(Test_SUT_MyClass, operator_multiplication_assignment_mocked)
{
   /* Arrange (pre-conditions and inputs */
   SUT_MyClass v1(MyNameSpace::MyClass(10));
   SUT_MyClass v2(MyNameSpace::MyClass(3));
   MyNameSpace::MyClass expected(3); // Only needed for the unparameterized test

   EXPECT_CALL(mockMyClass, Multiplication_assignment_operator(Matcher<const MyNameSpace::MyClass&>(Ref(expected))))
       .Times(1)
       .WillOnce(Return(29)); // 29 so we can check that the mock was used


   /* Act (on the SUT) */
   int return_value = v1.multiply_and_assignment_values(v2);

   /* Assert (that the expected results have occurred) */
   EXPECT_EQ(29, return_value);
}


TEST_F(Test_SUT_MyClass, operator_multiplication_assignment_other_type_mocked)
{
   /* Arrange (pre-conditions and inputs */
   SUT_MyClass v1(MyNameSpace::MyClass(10));
   double v2=3.0;
   double expected=3.0; // Only needed for the unparameterized test

   EXPECT_CALL(mockMyClass, Multiplication_assignment_operator(expected))
       .Times(1)
       .WillOnce(Return(31)); // 31 so we can check that the mock was used


   /* Act (on the SUT) */
   int return_value = v1.multiply_and_assignment_values_other_type(v2);

   /* Assert (that the expected results have occurred) */
   EXPECT_EQ(31, return_value);
}

Solution

  • Found the solution: Instead of Matcher I have to use A. Thus:

       EXPECT_CALL(mockMyClass, Multiplication_assignment_operator(A<const MyNameSpace::MyClass&>()))
           .Times(1)
           .WillOnce(Return(29));
    

    Does it for the first test case and

       EXPECT_CALL(mockMyClass, Multiplication_assignment_operator(A<double>()))
           .Times(1)
           .WillOnce(Return(31));
    

    For the 2nd.