Pergunta

The latest version of Hippo Mocks (in its Git repository) looks to have added support for COM interfaces. I've tried mocking an ADO connection object; which took some tweaking of Hippo Mocks to build properly (seems the COM version of the code wasn't updated for changes in the rest of Hippo Mocks). I have it building now, but the following test fails:

MockRepository mocks;
auto pConn = mocks.Mock<ADONS::_Connection>();
mocks.OnCall(pConn, ADONS::_Connection::AddRef).Return(1);

ADONS::_ConnectionPtr conn = pConn;

The very first thing the smart pointer does is AddRef the interface. My mock shouldn't care about reference counting, so I add a call expectation that simply returns 1. However, as soon as AddRef gets called, a HippoMocks::NotImplementedException gets thrown.

Has anyone had success with mocking a COM interface with Hippo Mocks?

Foi útil?

Solução

I had the same issue and solved it. The actual version of hippomocks is now published on github:

https://github.com/dascandy/hippomocks

Great appreciation for the provided links, which helped to find an idea for the fix.

UPDATE, Details about my implementation and the added COM support.

First I made it work, which the following test demonstrates

class ICom 
{
public:
    virtual ~ICom() {}
    virtual long __stdcall A(void) = 0;
    virtual long __stdcall B(int) = 0;
    virtual long __stdcall C(int, int) = 0;
    ...
};


TEST(checkStdCallBase)
{
    MockRepository mocks;

    ICom* ic = mocks.Mock<ICom>();
    mocks.ExpectCall(ic, ICom::A)
        .Return(1);

    long actual = ic->A();
    EQUALS(1, actual);
}

In order to make it work I had to patch several places in hippomocks.h, the most vital in virtual_function_index method. The correction ensures correct address calculation for the call on an interface.

Second, I added some common setup helpers for COM objects, providing standard behaviour for AddRef, Release and QueryInterface.

The tests show how to use it:

MIDL_INTERFACE("4745C05E-23E6-4c6d-B9F2-E483359A8B89")
COMInterface1 : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE getTObjectCount( 
        /* [out] */ unsigned long *pCount) = 0;
};

typedef GUID ESTypeID;

MIDL_INTERFACE("356D44D9-980A-4149-A586-C5CB8B191437")
COMInterface2 : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE getMappablePackages( 
        /* [out] */ long *pSize,
        /* [size_is][size_is][out] */ ESTypeID **pIdList) = 0;
};

        TEST(CheckThat_AddCommExpectations_Stubs_QueryInterface_AddRef_Release)
{
    MockRepository mocks; 
    COMInterface1* deviceMock = mocks.Mock<COMInterface1>();

    AddComExpectations(mocks, deviceMock);

    {
        CComPtr<IUnknown> pUnk = deviceMock;  
        CComQIPtr<COMInterface1> pDevice = pUnk;

        CHECK(pDevice == pUnk);

        IUnknown* p = NULL;
        pDevice->QueryInterface(__uuidof(IUnknown), (void**)&p);

        CHECK(p == deviceMock);
    }
}

TEST(CheckThat_ConnectComInterfaces_Stubs_QueryInterface_ToEachOther)
{
    MockRepository mocks; 
    COMInterface1* deviceMock = mocks.Mock<COMInterface1>();
    COMInterface2* devMappingMock = mocks.Mock<COMInterface2>();

    ConnectComInterfaces(mocks, deviceMock, devMappingMock);

    {
        //Com objects can reach each other
        CComQIPtr<COMInterface2> pDevMapping = deviceMock;

        CHECK(pDevMapping != NULL);
        CHECK(pDevMapping == devMappingMock);

        CComQIPtr<COMInterface1> pDevNavigate = devMappingMock;

        CHECK(pDevNavigate != NULL);
        CHECK(pDevNavigate == deviceMock);
    }

}

The helper methods AddComExpectations and ConnectComInterfaces are provided in a separate header "comsupport.h". The header is an add-on for Hippomocks:

template <typename T>
void AddComExpectations(HM_NS MockRepository& mocks, T* m)
{
    mocks.OnCall(m, T::AddRef)
        .Return(1);
    mocks.OnCall(m, T::Release)
        .Return(1);
    mocks.OnCall(m, T::QueryInterface)
        .With(__uuidof(T), Out((void**)m))
        .Return(S_OK);

    mocks.OnCall(m, T::QueryInterface)
        .With(__uuidof(IUnknown), Out((void**)m))
        .Return(S_OK);

}

template <typename T1, typename T2>
void ConnectComInterfaces(HM_NS MockRepository& mocks, T1* m1, T2* m2)
{
    //from T1 to T2
    mocks.OnCall(m1, T1::QueryInterface)
        .With(__uuidof(T2), Out((void**)m2))
        .Return(S_OK);
    //from T2 to T1
    mocks.OnCall(m2, T2::QueryInterface)
        .With(__uuidof(T1), Out((void**)m1))
        .Return(S_OK);

    AddComExpectations(mocks, m1);
    AddComExpectations(mocks, m2);

    //no support for interface hierarchies
    //no Base IUnknown -> do it yourself if you really need that special case
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top