Mock non-virtual method C++ (gmock)
Asked Answered
H

3

24

I have class

class CSumWnd : public CBaseWnd
{

 private:
 bool MethodA()
}

Please can you help how to mock MethodA() without making virtual, I didn't understand the concept of hi-perf dependency injection

Hydrazine answered 25/4, 2011 at 11:5 Comment(1)
Your link is dead - could you update it? I'm not able to find the exact same page, maybe this one is sufficient?Pestiferous
S
23

It means you will have to templatize your production code. Using your example:

CSumWind class definition:

class CSumWnd : public CBaseWnd
{

 private:
 bool MethodA()
};

Mocked CSumWnd class definition:

class MockCSumWnd : public CBaseWnd
{

 private:
 MOCK_METHOD(MethodA, bool());
};

Production class which have to be tested with mocked class CSumWind. Now it becomes templated to provide using CSumWind class in production code and MockCSumWnd class in tests.

template <class CSumWndClass>
class TestedClass {
//...
   void useSumWnd(const CSumWndClass &a);

private:
  CSumWndClass sumWnd;
};

Instantiation of TestedClass in production:

TestedClass <CSumWnd> obj;

Instantiation of TestedClass object in test executable:

TestedClass <MockCSumWnd> testObj;
Sessions answered 25/4, 2011 at 11:40 Comment(5)
To keep your 'production' code clean, I find it helpful to do this: template <class CSumWndClass> class TestedClassTemplate { ... }, and then do typedef TestedClassTemplate<CSumWndClass> TestedClass;Schoof
See https://mcmap.net/q/493990/-interfaces-vs-templates-for-dependency-injection-in-c/49972 for information on the consequences of doing what you propose.Adoree
I am facing issue in MOCK_METHOD(MethodA, bool()); #46542873 I am getting error specified in the question aboveTonality
MOCK_METHOD should be replaced by MOCK_METHOD0 // 0 parameters. There is should be ";" after "bool MethodA()" as well. Unfortunately, the idea is still not clear for me...Vareck
Mind writing the body of your method, to make it clear what it should do ? void useSumWnd(const CSumWndClass &a);Caesar
P
3

If you don't want to change the existing code, here is a specific solution for VC++ I'm working on (https://github.com/mazong1123/injectorpp). The brief steps are:

  1. Leverage DbgHelp.h to retrieve all methods' symbols and memory addresses of a class. Basically it retrieves meta info from .pdb file at runtime.
  2. Compare the user input to-mock method symbol with step 1's output, get the to-mock method's memory address.
  3. Leverage windows api WriteProcessMemory to change the entry bytes of the to-mock method, that is something similar as: __asm {move eax, 1; ret}.

Let's put key code here.

  1. Retrieve methods' symbols and addresses of a class. Below is the key idea of the implementation. The full source code is availiable at https://github.com/mazong1123/injectorpp/blob/master/injectorpp/ClassResolver.cpp

    // Retrieve class symbol.
    if (SymGetTypeFromName(this->m_hProcess, modBase, className.c_str(), classSymbol) == FALSE)
    {
        throw;
    }
    
    // Get children of class - which are methods.
    DWORD numChildren = 0;
    if (SymGetTypeInfo(this->m_hProcess, classSymbol->ModBase, classSymbol->TypeIndex, TI_GET_CHILDRENCOUNT, &numChildren) == FALSE)
    {
        throw;
    }
    
    // Get methods info.
    if (SymGetTypeInfo(this->m_hProcess, classSymbol->ModBase, classSymbol->TypeIndex, TI_FINDCHILDREN, methods) == FALSE)
    {
        throw;
    }
    
    // Retrieve all methods.
    for (DWORD i = 0; i < numChildren; ++i)
    {
        ULONG curChild = methods->ChildId[i];
    
        // Resolve function.
        Function resolvedFunction;
        this->m_functionResolver->Resolve(classSymbol->ModBase, curChild, resolvedFunction);
    
        // Add the resolved function to the output.
        resolvedMethods.push_back(resolvedFunction);
    }
    
  2. Step 2 is trival. It's only text comparing and processing.

  3. How to inject the magic asm to change the method behavior: (The full source code is available at https://github.com/mazong1123/injectorpp/blob/master/injectorpp/BehaviorChanger.cpp)

    // A magic function to change the function behavior at runtime
    //
    // funcAddress - The address of the function to be changed from.
    // expectedReturnValue - The return value should be changed to.
    void BehaviorChanger::ChangeFunctionReturnValue(ULONG64 funcAddress, int expectedReturnValue)
    {
    
    
    // The purpose of this method is to change the return value
    // to what ever int value we expected.
    
    // Therefore, we just need to inject below asm to the header of specific function:
    //
    // mov eax, expectedValue
    // ret
    //
    // Above asm code tells the function to return expectedValue immediately.
    
    // Now let's prepare the asm command.
    byte asmCommand[6];
    
    // mov
    asmCommand[0] = 0xB8;
    
    // The value.
    asmCommand[1] = expectedReturnValue & 0xFF;
    asmCommand[2] = (expectedReturnValue >> 8) & 0xFF;
    asmCommand[3] = (expectedReturnValue >> 16) & 0xFF;
    asmCommand[4] = (expectedReturnValue >> 24) & 0xFF;
    
    // ret
    asmCommand[5] = 0xC3;
    
    WriteProcessMemory((HANDLE)-1, (void*)funcAddress, asmCommand, 6, 0);
    }
    
Puzzler answered 28/5, 2016 at 17:0 Comment(2)
Looks interesting. I would love to see something similar for GCC/Clang.Bellows
@Bellows I'm planning to do this. Recently I've almost finished the x86 Windows part. Stay tune.Puzzler
L
2

Try CppFreeMock and some others mentioned here.

Example:

string func() {
    return "Non mocked.";
}

TEST(HelloWorld, First) {
    EXPECT_CALL(*MOCKER(func), MOCK_FUNCTION()).Times(Exactly(1))
        .WillOnce(Return("Hello world."));
    EXPECT_EQ("Hello world.", func());
}
Longsome answered 28/9, 2014 at 4:38 Comment(4)
That second link for me points to a OneDrive login. Also rather than using just "here" as a description it would be better to put something more descriptive so the document can still be found if the link dies in the future.Enliven
I don't have enough reputation to add more than 2 links in one answer, update the link of "here", no need login any more. The full doc can be find in github.Longsome
looks like it fails to compile with the current gmockVareck
@Longsome It seems to me that you are the author of CppFreeMock. You should disclose that in the answer.Ofori

© 2022 - 2024 — McMap. All rights reserved.