Search code examples
c++unit-testingopencvgoogletestgooglemock

GMock : error: cannot convert ‘cv::MatExpr’ to ‘bool’ in return


Context : I am trying to Mock an OpenCV-C++ class using GMock.

Problem :

I am not able to use the EXPECT_CALL method for a function that takes in a cv::Mat and returns a cv::Mat. The compiler says that the gmock-matcher cannot convert from cv::MatExpr to bool in return.

The following are the detailed error messages during compile time.

In file included from /home/arun/Documents      /LaneDetection/test/../vendor/googletest/googlemock/include/gmock/gmock-spec-builders.h:75:0,
             from /home/arun/Documents/LaneDetection/test/../vendor/googletest/googlemock/include/gmock/gmock-generated-function-mockers.h:43,
             from /home/arun/Documents/LaneDetection/test/../vendor/googletest/googlemock/include/gmock/gmock.h:61,
             from /home/arun/Documents/LaneDetection/test/test.cpp:32:
/home/arun/Documents/LaneDetection/test/../vendor    /googletest/googlemock/include/gmock/gmock-matchers.h: In instantiation of ‘bool     
testing::internal::AnyEq::operator()(const A&, const B&) const [with A = cv::Mat; B = cv::Mat]’:
/home/arun/Documents/LaneDetection/test/../vendor/googletest/googlemock/include/gmock/gmock-matchers.h:908:18:   
required from ‘bool testing::internal::ComparisonBase<D, Rhs, Op>::Impl<Lhs>::MatchAndExplain(Lhs, testing::MatchResultListener*) const [with Lhs = cv::Mat; D = testing::internal::EqMatcher<cv::Mat>; Rhs = cv::Mat; Op = testing::internal::AnyEq]’
/home/arun/Documents/LaneDetection/test/test.cpp:77:39:   required from here
/home/arun/Documents/LaneDetection/test/../vendor/googletest/googlemock/include/gmock/gmock-matchers.h:204:63: error: cannot convert ‘cv::MatExpr’ to ‘bool’ in return
   bool operator()(const A& a, const B& b) const { return a == b; }
                                                               ^
test/CMakeFiles/cpp-test.dir/build.make:86: recipe for target 'test/CMakeFiles/cpp-test.dir/test.cpp.o' failed
make[2]: *** [test/CMakeFiles/cpp-test.dir/test.cpp.o] Error 1
CMakeFiles/Makefile2:178: recipe for target 'test/CMakeFiles/cpp-test.dir/all' failed
make[1]: *** [test/CMakeFiles/cpp-test.dir/all] Error 2
Makefile:127: recipe for target 'all' failed
make: *** [all] Error 2

The following is my mocked class:

Thresholder.hpp

class Thresholder {
 public:

    Thresholder() {}
    virtual ~Thresholder() {} 
    virtual cv::Mat convertToLab(cv::Mat smoothImg);

 private:
    cv::Mat inputImg;   // < Container used for storing input image
    cv::Mat labImage;   // < Container for LAB converted input image
};

Thresholder.cpp

cv::Mat Thresholder::convertToLab(cv::Mat smoothImg) {
    inputImg = smoothImg;
    cv::cvtColor(inputImg, labImage, cv::COLOR_BGR2Lab);
    return labImage;
}

test.cpp

class MockThresholder : public Thresholder {
public:
    MOCK_METHOD1(convertToLab, cv::Mat(cv::Mat smoothImg));
};

/*
*@brief  : Creating test cases for mock class
*/

TEST(MockTest, ThreshTest) {
MockThresholder ThreshMock;
cv::Mat dummyXY = cv::Mat::ones(100, 100, CV_8UC3);
EXPECT_CALL(ThreshMock, convertToLab(dummyXY))
.Times(1)
.WillOnce(::testing::Return(dummyXY));}

Question :

There seems to be no online resource which demonstrates how to effectively use OpenCV and C++ with gmock. Would it be possible to share some demonstrations of how to test a C++ class-method that returns cv::Mat in GMock ?


Solution

  • The problem here is not the returning type, but the expected call. Specifically EXPECT_CALL(ThreshMock, convertToLab(dummyXY)) makes GMock check if the called parameter is indeed equal to dummyXY. By default it uses the == comparision operator.

    But OpenCV declares their comparison as cv::MatExpr operator==(cv::Mat, cv::Mat). It returns a matrix of booleans instead of a bool.

    Thus, you have to tell GMock how to match your expected call with a custom matcher. You create matchers with use of MATCHER_... macros:

    MATCHER_P(cvMatMatches, expected, "Match arg cvMat to be equal to expected") {
      if (arg.size() != expected.size()) {
        return false;
      }
      auto differingElems = (arg != expected);
      return cv::countNonZero(differingElems) == 0;
    }
    

    And your test code becomes:

    TEST(MockTest, ThreshTest) {
      MockThresholder ThreshMock;
      cv::Mat dummyXY = cv::Mat::ones(100, 100, CV_8U);
      EXPECT_CALL(ThreshMock, convertToLab(cvMatMatches(dummyXY)))
          .Times(1)
          .WillOnce(::testing::Return(dummyXY));
    
      ThreshMock.convertToLab(dummyXY);
    }