rollbear / trompeloeil

Header only C++14 mocking framework
Boost Software License 1.0
802 stars 85 forks source link

How can i mock a member function of A class in a B class's function? #309

Open git45016683 opened 1 year ago

git45016683 commented 1 year ago

This is a common scenario: class B has an A object, so class B member function can call class A member function with A object. so how can i mock class A member function when i perform unit testing on class B member function? example code like below:

class memberFuncHeader
{
public:
    void vOutputValueByPointer(char* p_cStr)
        {
            p_cStr[0] = 'o'; p_cStr[1] = 'r'; p_cStr[2] = 'i';
        p_cStr[3] = 'g'; p_cStr[4] = 'i'; p_cStr[5] = 'n';
        }
}

class callMemberFunc
{
public:
  int iTest_OutputValueByPointer(char* p_cStr){
        m_pObj->vOutputValueByPointer(p_cStr);
    return 0;
  }
public:
    memberFuncHeader* m_pObj;
}

the unit test code like below:

class memberFuncMock {
public:
    MAKE_MOCK1(vOutputValueByPointer, void(char*));
};
memberFuncMock memberMock;

callMemberFunc* g_pCallMemberFunc = new callMemberFunc();

int funcCall_OutputValueByPointer(char* p_pcStr) {
    int l_iRet = -1;
    char l_acStr[256] = {0};
    l_iRet = g_pCallMemberFunc->iTest_OutputValueByPointer(l_acStr);
    memcpy(p_pcStr, l_acStr, 256);
    return l_iRet;
}

TEST_CASE("demo2mockMemberFunc0") {
    char* l_pStr = new char[256];
    memcpy(l_pStr, "HELLOWORLD", strlen("HELLOWORLD"));
    REQUIRE_CALL(memberMock, vOutputValueByPointer(_)).LR_SIDE_EFFECT(memcpy(_1, l_pStr, strlen(l_pStr)));

    char l_acStr[256] = { 0 };
    int l_iRet = funcCall_OutputValueByPointer(l_acStr);
    CHECK(l_iRet == 0);
    CHECK(memcmp(l_acStr, "HELLOWORLD", strlen("HELLOWORLD") == 0));
}

I expect iTest_OutputValueByPointer call vOutputValueByPointer mock function and l_acStr equal “HELLOWORLD”, but iTest_OutputValueByPointer actually call class memberFuncHeader function then output 'origin' to l_acStr.

can do this with trompeloeil? and i push the demo code at demo

rollbear commented 1 year ago

You need to make the A member accessible in the test so that you can place an expectation on it. One traditional technique for this is dependency injection, i.e. you create your B with the mocked A for the test.

git45016683 commented 1 year ago

Yeah, I know what you mean, but that's a real huge workload, because there's a lot of historical code (T_T). And they are running at product.... So, i want to found some solution that can integrated unit testing without requiring too many source code changes。 There are many codes like demo in historical code。Although not the best, but it work。 So i can not change every class to dependency injection。 Thanks。

rollbear commented 1 year ago

Well, the library can only work within the rules of the language. For this to work you need:

  1. To ensure that in the test, the member m_pObj points to a mock object.
  2. Access to the mock object from the test code, and its type known, at compile time, in the test to be a mock.

With those you can place expectations on the mock and test your code. If those are not acceptable, then I'm afraid I cannot help.