Search code examples
multithreadingunit-testingmockinghippomocks

Is HippoMocks thread-safe?


Could HippoMocks be used within concurrent testcases just like this:

  1. synchronized startup phase

    • create mock
    • register expectations etc.
  2. parallel testing phase

    • call methods on the mock
  3. synchronized teardown phase

    • verify the mock

I did not find an explicit statement regarding this issue. Here and there it is mentioned, that mocking non-virtual methods would destroy the possibility for thread-safety (HippoMocks: is it possible to mock non-virtual methods?) or that thread-safety could be added quite easily (unfortunately without actually revealing how). GoogleMock answers this question very clearly (https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md#using-google-mock-and-threads), such an information would be helpful here, too.


Solution

  • No, HippoMocks is not designed to be thread safe.

    However, if you follow some simple rules you should be able to use mocks in a multithreaded environment:

    1. Do the setup sequentially in one thread and use one single MockRepository.
    2. Using different mocks in different threads should be safe.
    3. Using one mock in different threads is safe, when you use only OnCall() setups. Combine it with OnCall().Do() and you should be able do a lot of testing this way.
    4. Don't use ExpectCall - it is not safe.

    UPDATE: Okay, I did it. I wrote a small test for multithreading

    class IMulti
    {
    public:
        virtual void A() =0;
        virtual int B(int a) = 0;
    };
    
    const int THREAD_ITERATIONS = 1000;
    
    static DWORD WINAPI run_thread(LPVOID args)
    {
        IMulti* im = static_cast<IMulti*>(args);
        for (int i=0; i<THREAD_ITERATIONS; i++)
        {
            im->A();
            int result = im->B(22);
            std::cout << "task says: " << i <<" result:" << result <<"\n";
        }
    
        std:: cout << "finished";
    
        return 0;
    }
    
    TEST(check_HippoMocksCanMultiThreadedConcurrentReadingViaOnCall)
    {
        MockRepository mocks;
    
        IMulti* im = mocks.Mock<IMulti>();
    
        mocks.OnCall(im, IMulti::A);
        mocks.OnCall(im, IMulti::B).Return(4711);
    
        HANDLE handles[2];
    
        handles[0] = CreateThread(NULL, 0, &run_thread, im, 0, NULL);
        handles[1] = CreateThread(NULL, 0, &run_thread, im, 0, NULL);
    
        WaitForMultipleObjects(2, handles, TRUE, INFINITE);
    }
    

    The result is, that it works fine. Now I made it a little bit harder and replaced the second OnCall by the following:

    for (int i = 0; i< THREAD_ITERATIONS*2; i++)
    {
        mocks.ExpectCall(im, IMulti::B).Return(i);
    }
    

    Here you will get crashes randomly (just play around with the THREAD_ITERATIONS counter). The reason is, that the matched expectations are somehow counted in the Mockrepository.

    Doing the setup concurently crashes, as expected.