[gtest] Singleton mocking하기

달피·2021년 5월 2일
0

Singleton은 인터페이스를 통해 추상화하지 않고 해당 클래스를 그대로 참조하므로 mock을 만드는데 어려움이 있다.
#MockingNonVirtualMethods 를 활용하면 template를 통해 mocking이 가능하도록 만들 수 있다.

1. Class 이름이 typename이 되도록 수정하기

일단 Singleton과 이를 사용하는 User에 대한 코드이다.
Singleton에 대한 구현은 간단한 Singleton 작성하기를 참고

class Singleton
{
public:
    static Singleton& getInstance()
    {
        static Singleton instance;
        return instance;
    }
    void callFunction() { /*do something*/ } //테스트하고자 하는 함수, 심지어 가상함수도 아니다.
};
class User
{
public:
    void act()
    {
        Singleton::getInstance().callFunction(); //당연하지만 "Singleton"은 class이름으로 사용되고 있다
    }
};

User 클래스는 Singleton을 직접 참조하고 있으므로 Singleton 클래스를 mock으로 대체하여 테스트할 수 없는 상태이다.

이 때 Singleton의 클래스이름이 typename이 되도록 테스트하는 코드에 template을 적용한다.
(Singleton 을 흔히 사용하는 T 가 된다고 생각하면 편하다)
이렇게 하면 User::act() 함수 안에서는 typename Singleton을 만족하는 모든 타입들에 대해 동일하게 callFunction()을 호출할 수 있게 된다.

template <typename Singleton>
class User
{
public:
    void act()
    {
        Singleton::getInstance().callFunction(); //이제부터 "Singleton"은 typename이다!
    }
};

2. typename에 맞게 mocking하기

동일하게 동작하도록 getInstance() 를 만들어주고 테스트할 함수인 callFunction()을 mocking 해 준다.

class MockSingleton //Singleton을 상속받는 것이 아니다!
{
public:
    static MockSingleton& getInstance() //getInstance의 함수 이름이 있어야 하며 원래의 class Singleton처럼 동작하도록 작성한다.
    {
        static MockSingleton instance;
        return instance;
    }
    MOCK_METHOD(void, callFunction, ()); //Singleton을 상속받는 것이 아니므로 override가 아니다
};

TEST(UserTest, SingletonCallTest)
{
    User<MockSingleton> user;
    EXPECT_CALL(MockSingleton::getInstance(), callFunction).Times(1);

    user.act(); //callFunction()이 한 번 호출되어 pass된다.
}

테스트하고자 하는 원본 코드에도 수정이 가해야져 하는 단점이 있지만 Singleton을 mocking할 수 있다는 점에서 쓸만해 보인다.


profile
개발 오답노트

1개의 댓글

comment-user-thumbnail
2021년 6월 23일

간사합니다....

싱글톤 클래스 사용처에서 처음부터 template를 썼어야 했네요 ㅎㅎ;;

답글 달기