BonoboMock is a GoogleTest-compatible C++ mocking library that uses runtime binary patching (aka monkey patching) to intercept function calls at runtime. Unlike traditional mocking frameworks, it can mock free functions, static methods, non-virtual and virtual member functions, and even private/protected methods – without requiring the C++ code under test to be designed for testability.
If the animation above does not display, view it here.
Most C++ components depend on other components – classes in the same project, linked libraries, databases, or remote services, for example. To unit-test in isolation, you must replace those dependencies with test doubles.
Consider a class like this:
class CompanyUtil {
public:
// Business logic we want to test
static bsl::string produceDividendMessage(int companyID) {
bsl::string name = getCompanyNameFromDb(companyID);
bsl::string region = getRegionFromService(companyID);
// Business logic combining results
return "Company " + name + " (" + region + ") issued a dividend.";
}
static bsl::string getCompanyNameFromDb(int companyID) {
// Hits a real database
}
static bsl::string getRegionFromService(int companyID) {
// Calls a remote service
}
};
Business logic is intertwined with hard-coded dependencies, and it is fully implemented as static functions. There is no way to replace the database or service calls with test doubles without making changes to the code or the build pipeline. As a result, the business logic cannot be tested in isolation. You may often encounter this pattern in legacy and/or third-party code.
There are three common strategies to test code with hard dependencies like these:
Define an abstract interface for the dependency, provide a production implementation and a test mock using gMock, and inject the dependency via constructor or setter.
Trade-offs:
Provide an alternative translation unit with a different implementation of the dependency, and then link the test binary against it instead of the production version.
Trade-offs:
Write and maintain a hand-written mock class for each dependency. Use the mock in tests and the real class in production.
Trade-offs:
All three approaches require the code to be structured upfront for testability, or to undergo significant refactoring – which is not always feasible for the software you may be seeking to test.
BonoboMock patches the target function’s entry point in memory at runtime, redirecting calls to a gMock-based handler. The original instructions are restored when the mock goes out of scope (RAII). This means:
EXPECT_CALL / WillOnce / Return API you already know from gMock.For example, testing CompanyUtil with BonoboMock requires zero refactoring:
TEST(CompanyUtilTest, ProduceDividendMessage) {
auto mockDb = BONOBO_MOCK(&CompanyUtil::getCompanyNameFromDb);
auto mockSvc = BONOBO_MOCK(&CompanyUtil::getRegionFromService);
EXPECT_CALL(*mockDb, BONOBO_MOCK_FUNCTION(42))
.WillOnce(Return("Acme Corp"));
EXPECT_CALL(*mockSvc, BONOBO_MOCK_FUNCTION(42))
.WillOnce(Return("AMER"));
EXPECT_EQ("Company Acme Corp (AMER) issued a dividend.",
CompanyUtil::produceDividendMessage(42));
}
For a detailed explanation of the patching mechanism, gMock integration, and call interception flow, see Internals.
Follow this priority order when deciding how to test code with dependencies:
Refactor for testability (preferred). If you can modify the code, introduce abstractions and dependency injection so dependencies can be replaced with standard gMock test doubles. For CompanyUtil, this would mean extracting the database and service calls behind injectable interfaces so that tests can supply mock implementations directly.
Wrap in an interface. When you depend on someone else’s component (or are dealing with legacy code you can’t change), write a small wrapper class that exposes the functionality behind an interface. Your code depends on the interface, not the original component, allowing you to inject a mock implementation during testing. If CompanyUtil belongs to a library you cannot modify, you could wrap it in your own thin adapter class that forwards calls to CompanyUtil but can be swapped for a mock in tests.
Use BonoboMock. When refactoring or wrapping is too costly, too risky, or impractical – e.g., deeply embedded legacy code, third-party libraries, or code too fragile to change safely – BonoboMock lets you mock the dependency directly without any code changes.
Do not use BonoboMock as a design strategy for new components. New code should be designed with testability in mind using dependency injection and interfaces.
BonoboMock’s ability to mock any function should never justify writing tightly coupled or non-modular code. If you are writing new code, it is recommended that you design it for testability from the start.