Mocking Singleton Template Classes with Google Mock

This guide demonstrates techniques for mocking singleton template classes using Google Mock.

Method 1: Direct Mocking via Static Method

For a simple singleton template class, you can directly mock its methods using GMock's MOCK_METHOD macro. This approach assumes the singletno class already exposes methods intended for mocking.

#include <gtest/gtest.h>
#include <gmock/gmock.h>

using ::testing::Return;
using ::testing::AnyNumber;
using ::testing::_;

template<typename T>
class MockSingletonFoo {
public:
    // Static method to access the singleton instance
    static MockSingletonFoo& GetInstance() {
        static MockSingletonFoo instance;
        return instance;
    }

    // GMock method for mocking
    MOCK_METHOD(T, performOperation, (const T&), ());

private:
    // Private constructor and destructor to enforce singleton pattern
    MockSingletonFoo() = default;
    ~MockSingletonFoo() = default;
    MockSingletonFoo(const MockSingletonFoo&) = delete;
    MockSingletonFoo& operator=(const MockSingletonFoo&) = delete;
};

// Test fixture for Foo tests
class FooTest : public ::testing::Test {
protected:
    void SetUp() override {
        // No specific setup needed for this basic case
    }
    void TearDown() override {
        // No specific teardown needed for this basic case
    }
};

// Test case for mocking a singleton operation
TEST_F(FooTest, MockOperation)
{
    auto& singletonInstance = MockSingletonFoo<int>::GetInstance();
    // Expect the performOperation method to be called once with any argument, returning 42
    EXPECT_CALL(singletonInstance, performOperation(_)).WillOnce(Return(42));

    // Assert that calling the method returns the mocked value
    EXPECT_EQ(singletonInstance.performOperation(10), 42);
}

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Installasion (Red Hat-based systems):

sudo dnf install gtest gtest-devel gmock-devel

Compilation:

g++ -std=c++17 main.cpp -lgtest -lgmock -lpthread -o test

Method 2: Using a Mockable Base Class with Instance Replacement

In scenarios where the original singleton class doesn't expose mockable methods directly, you can create a derived mock class and provide a mechanism to replace the singleton instance during testing.

Using std::unique_ptr:

This approach uses a std::unique_ptr to manage the singleton instance, preventing potential memory leaks and simplifying instance management.

#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <memory>

using ::testing::_;
using ::testing::Return;

template <typename T>
class SingletonBase {
private:
    static std::unique_ptr<SingletonBase> instance_ptr; // Use unique_ptr for safer memory management

public:
    // Static method to get the singleton instance
    static SingletonBase& GetInstance() {
        if (!instance_ptr) {
            instance_ptr = std::make_unique<SingletonBase>();
        }
        return *instance_ptr;
    }

    // Static method to replace the singleton instance for testing
    static void SetInstance(std::unique_ptr<SingletonBase> newInstance) {
        instance_ptr = std::move(newInstance);
    }

    // Virtual method to allow overriding in mock class
    virtual T processValue(const T& input) {
        return input; // Default implementation
    }

    // Public constructor and virtual destructor are essential for polymorphism and proper cleanup
    SingletonBase() = default;
    virtual ~SingletonBase() = default;

    // Disable copy and assignment to maintain singleton property
    SingletonBase(const SingletonBase&) = delete;
    SingletonBase& operator=(const SingletonBase&) = delete;
};

// Definition of the static member
template <typename T>
std::unique_ptr<SingletonBase<T>> SingletonBase<T>::instance_ptr = nullptr;

// Derived mock class inheriting from the base singleton
template <typename T>
class MockSingleton : public SingletonBase<T> {
public:
    // Mock the virtual method
    MOCK_METHOD(T, processValue, (const T&), (override));
};

// Test fixture for SingletonBase tests
class SingletonBaseTest : public ::testing::Test {
protected:
    void SetUp() override {
        // Setup can be empty or customized as needed
    }
    void TearDown() override {
        // Teardown can be empty or customized as needed
    }
};

// Test case 1: Verify mocking with unique_ptr
TEST_F(SingletonBaseTest, MockWithUniquePtr1)
{
    // Create a mock instance using unique_ptr
    auto mockInstance = std::make_unique<MockSingleton<int>>();
    // Set expectations on the mock object
    EXPECT_CALL(*mockInstance, processValue(_)).WillOnce(Return(42));

    // Replace the actual singleton instance with the mock instance
    SingletonBase<int>::SetInstance(std::move(mockInstance));

    // Call the method on the singleton instance and assert the result
    EXPECT_EQ(SingletonBase<int>::GetInstance().processValue(1), 42);
}

// Test case 2: Another test to ensure isolation
TEST_F(SingletonBaseTest, MockWithUniquePtr2)
{
    auto mockInstance = std::make_unique<MockSingleton<int>>();
    EXPECT_CALL(*mockInstance, processValue(_)).WillOnce(Return(43));
    SingletonBase<int>::SetInstance(std::move(mockInstance));
    EXPECT_EQ(SingletonBase<int>::GetInstance().processValue(2), 43);
}

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Using Raw Pointers (Caution Advised):

If std::unique_ptr causes issues or is not suitable, a raw pointer can be used. However, this requires careful manual memory management to avoid leaks and segmentation faults.

#include <gtest/gtest.h>
#include <gmock/gmock.h>

using ::testing::_;
using ::testing::Return;

template <typename T>
class RawSingleton {
private:
    static RawSingleton* instance_ptr;

public:
    static RawSingleton& GetInstance() {
        if (!instance_ptr) {
            instance_ptr = new RawSingleton();
        }
        return *instance_ptr;
    }

    // Method to replace the singleton instance
    static void SetInstance(RawSingleton* newInstance) {
        delete instance_ptr; // Clean up the old instance
        instance_ptr = newInstance;
    }

    // Method to release the singleton instance (optional, useful for cleanup)
    static void ReleaseInstance() {
        delete instance_ptr;
        instance_ptr = nullptr;
    }

    virtual T processRawValue(const T& input) {
        return input; // Default implementation
    }

    RawSingleton() = default;
    virtual ~RawSingleton() = default; // Virtual destructor is crucial

    RawSingleton(const RawSingleton&) = delete;
    RawSingleton& operator=(const RawSingleton&) = delete;
};

// Definition of the static member
template <typename T>
RawSingleton<T>* RawSingleton<T>::instance_ptr = nullptr;

// Mock class inheriting from RawSingleton
template <typename T>
class MockRawSingleton : public RawSingleton<T> {
public:
    MOCK_METHOD(T, processRawValue, (const T&), (override));
};

// Test fixture for RawSingleton tests
class RawSingletonTest : public ::testing::Test {
protected:
    MockRawSingleton<int>* mock_ptr = nullptr;

    void SetUp() override {
        // Create a mock instance and set it as the singleton instance
        mock_ptr = new MockRawSingleton<int>();
        RawSingleton<int>::SetInstance(mock_ptr);
    }

    void TearDown() override {
        // Reset the singleton instance and clean up the mock object
        RawSingleton<int>::ReleaseInstance(); // Important to prevent memory leaks
        // delete mock_ptr; // SetInstance already deleted the previous instance, and ReleaseInstance nullifies it. Manual delete here could be problematic if not handled carefully.
    }
};

// Test case 1: Verify mocking with raw pointers
TEST_F(RawSingletonTest, MockWithRawPointer1)
{
    // Set expectations on the mock object
    EXPECT_CALL(*mock_ptr, processRawValue(_)).WillOnce(Return(42));
    // Call the method on the singleton instance and assert the result
    EXPECT_EQ(RawSingleton<int>::GetInstance().processRawValue(1), 42);
}

// Test case 2: Another test case for isolation
TEST_F(RawSingletonTest, MockWithRawPointer2)
{
    EXPECT_CALL(*mock_ptr, processRawValue(_)).WillOnce(Return(43));
    EXPECT_EQ(RawSingleton<int>::GetInstance().processRawValue(2), 43);
}

int main(int argc, char** argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Choosing between std::unique_ptr and raw pointers depends on the specific constraints and memory management strategy of your project. std::unique_ptr general offers safer and more idiomatic C++ memory handling.

Tags: C++ gmock singleton template testing

Posted on Tue, 12 May 2026 22:21:15 +0000 by onlinegs