Search code examples
c++googletestgooglemock

How to do the partial match for a struct parameter in gmock expect_call


struct obj
{
  int a;
  string str;
  string str2;
  bool operator==(const obj& o) const
  {
     if(a == o.a && str == o.str && str2 == o.str2) return true;
     return false;
   } 
}

Then in a function in a class, it is using struct object as input parameters:

bool functionNeedsToBeMocked(obj& input)
{
  //do something
}

Now what I want to do is,

EXPECT_CALL(*mockedPointer, functionNeedsToBeMocked( /* if input.a == 1 && input.str == "test" && input.str2.contains("first")*/  )).Times(1).WillOnce(Return(true));

And the input value is

inputFirst.a = 1;
inputFirst.str = "test";
inputFirst.str2 = "something first";

I expect the inputFirst can be matched to my EXPECT_CALL.

How could I using EXPECT_CALL matcher to do that ?

I did see

EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")),
                      NULL));

on gmock cookbook, but I don't know how to do HasSubStr for a struct parameter.


Solution

  • You can implement your own matcher for the obj struct.

    When you type:

    EXPECT_CALL(*mockedPointer, functionNeedsToBeMocked(some_obj)).Times(1).WillOnce(Return(true));
    

    then gmock is using the default matcher, Eq, using some_obj as its expected argument and the actual functionNeedsToBeMocked argument as arg in the matcher. Eq matcher will by default call bool operator== for the expected and actual objects:

    EXPECT_CALL(*mockedPointer, functionNeedsToBeMocked(Eq(some_obj))).Times(1).WillOnce(Return(true));
    

    However, as you don't want to use the bool operator==, you can write a custom matcher (removing Times(1) as it is the default one as well):

    // arg is passed to the matcher implicitly
    // arg is the actual argument that the function was called with
    MATCHER_P3(CustomObjMatcher, a, str, str2, "") {
      return arg.a == a and arg.str == str and (arg.str2.find(str2) != std::string::npos); 
    }
    [...]
    EXPECT_CALL(*mockedPointer, functionNeedsToBeMocked(CustomObjMatcher(1, "test", "first"))).WillOnce(Return(true));
    

    There's a possibility to compose a custom matcher usig Field matchers and in-built matchers as HasString but let's 'leave it as an excercise to the reader' :P

    Update: Full blown code with Field matchers:

    struct obj {
        int a;
        std::string str;
        std::string str2;
    };
    
    struct Mock {
        MOCK_METHOD(bool, functionNeedsToBeMocked, (obj&));
    };
    
    // creates a matcher for `struct obj` that matches field-by-field
    auto Match(int expected_a, std::string expected_str1, std::string expected_substr2) {
        return testing::AllOf(
                testing::Field(&obj::a, expected_a),
                testing::Field(&obj::str, expected_str1),
                testing::Field(&obj::str2, testing::HasSubstr(expected_substr2))
        );
    }
    
    TEST(MyTest, test) {
        Mock mock{};
    
        obj inputFirst;
        inputFirst.a = 1;
        inputFirst.str = "test";
        inputFirst.str2 = "something first";
    
        EXPECT_CALL(mock, functionNeedsToBeMocked(Match(1, "test", "first"))).Times(1).WillOnce(Return(true));
    
        mock.functionNeedsToBeMocked(inputFirst);
    }