Search code examples
c++visual-studio-2010mockinghippomocks

Can a mock be reused for multiple expected calls with different return values in Hippo Mocks?


I'm using Hippo Mocks to great success, but I have a situation that I can't quite figure out how to set up properly. The code under test looks sorta like this:

auto firstName = record.at("firstName").getValue();
auto lastName = record.at("lastName").getValue();

Where IRecord::at() returns a IColumn&, which has a pure virtual getValue() method. I'm trying to test my code with mocked versions of Irecord and IColumn:

auto mockRec = mocks.InterfaceMock<IRecord>();
auto mockCol = mocks.InterfaceMock<IColumn>();

I can set up the expectations for firstName just fine:

mocks.OnCall(mockRec, IRecord::at).With("firstName").Return(std::ref(*mockCol));
mocks.OnCall(mockCol, IColumn::getValue).Return(std::string("John")));

But I would like to reuse the IColumn mock for the next expectation for lastName.

mocks.OnCall(mockRec, IRecord::at).With("lastName").Return(std::ref(*mockCol));
mocks.OnCall(mockCol, IColumn::getValue).Return(std::string("Doe")));

But when I run this, Hippo Mocks returns "John" for both getValue() calls.

Following along with the tutorial, I tried to restrict the order of "firstName" and "lastName" calls:

auto& firstCall = mocks.OnCall(mockRec, IRecord::at).With("firstName").Return(std::ref(*mockCol));
mocks.OnCall(mockCol, IColumn::getValue).After(firstCall).Return(std::string("John")));

auto& lastCall = mocks.OnCall(mockRec, IRecord::at).With("lastName").Return(std::ref(*mockCol));
mocks.OnCall(mockCol, IColumn::getValue).After(lastCall).Return(std::string("Doe")));

But I still get "John" for both getValue() calls.

Q: Is it possible to reuse the IColumn interface and tell Hippo Mocks to return different values with each getValue() call like I need, or am I stuck creating a separate IColumn mock for each parameter? Note: My actual implementation will have more than two parameters, so reusing the IColumn mock reduces a lot of setup for each unit test.


Solution

  • I'm not sure what the problem in your case is, but when I run the following code with the version form the git repoitory

    struct IColumn {
        virtual std::string getValue() = 0;
    }; 
    
    struct IRecord {
        virtual IColumn& at( std::string ) = 0;
    };
    
    void main()
    {
        MockRepository mocks;
    
        auto mockRec = mocks.Mock<IRecord>();
        auto mockCol = mocks.Mock<IColumn>();
    
        auto& firstCall = mocks.OnCall(mockRec, IRecord::at).With("firstName").Return(std::ref(*mockCol));
        mocks.OnCall(mockCol, IColumn::getValue).After(firstCall).Return(std::string("John"));
    
        auto& lastCall = mocks.OnCall(mockRec, IRecord::at).With("lastName").Return(std::ref(*mockCol));
        mocks.OnCall(mockCol, IColumn::getValue).After(lastCall).Return(std::string("Doe"));
    
        std::cout << mockRec->at("firstName").getValue() << " " 
                  << mockRec->at("lastName").getValue() << "\n";
    }
    

    I get the correct output.

    John Doe
    

    I find that I almost always use

    mocks.autoExpect = false;
    

    but in this case it doesn't make any difference.

    Edit:

    If you require more flexibility, you can do something like this:

    std::vector<IColumn*> cols;
    
    cols.push_back( mocks.Mock<IColumn>() );
    cols.push_back( mocks.Mock<IColumn>() );
    
    mocks.OnCall(mockRec, IRecord::at).With("firstName")
         .Return(std::ref(*cols[0]));
    mocks.OnCall(mockRec, IRecord::at).With("lastName")
         .Return(std::ref(*cols[1]));
    
    mocks.OnCall(cols[0], IColumn::getValue)
         .Return(std::string("John"));
    mocks.OnCall(cols[1], IColumn::getValue)
         .Return(std::string("Doe"));
    

    which will work in any order of the calls. Alternatively you can also use Do

    std::map<std::string, IColumn*> map;
    
    map["firstName"] = mocks.Mock<IColumn>();
    map["lastName"] = mocks.Mock<IColumn>();
    
    mocks.OnCall(mockRec, IRecord::at)
         .Do( [&map]( std::string& key){ return std::ref(*map[key]); } );
    
    mocks.OnCall(map["firstName"], IColumn::getValue)
         .Return(std::string("John"));
    mocks.OnCall(map["lastName"], IColumn::getValue)
         .Return(std::string("Doe"));