Mock non-virtual method C++ (gmock)

后端 未结 3 2006
逝去的感伤
逝去的感伤 2020-12-08 08:01

I have class

class CSumWnd : public CBaseWnd
{

 private:
 bool MethodA()
}

Please can you help how to mock MethodA() without

相关标签:
3条回答
  • 2020-12-08 08:43

    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);
      }
      
    0 讨论(0)
  • 2020-12-08 08:55

    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());
    }
    
    0 讨论(0)
  • 2020-12-08 09:07

    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;
    
    0 讨论(0)
提交回复
热议问题