본문 바로가기

에러 해결

[문제 해결] static 메서드를 mocking 해보자

문제

JUnit과 Mockito를 사용해서 테스트를 하던 중 static 메서드를 mocking해야 하는 상황이 생겼다.

테스트하고 있던 부분은 JWT 토큰을 생성하고 검증하는 로직으로 

여러 곳에서 호출해서 쓰인다고 생각해서 static 메서드로 만들고 common 패키지에 위치시켰다.

public class JwtUtil {

    public static void verifyToken(String jwtToken) {
        JWT.require(Algorithm.HMAC512(JWT_SECRET)).build().verify(jwtToken);
    }

    public static String createToken(Long userId, String email)  {
        return JWT.create()
                .withClaim("id", userId)
                .sign(Algorithm.HMAC512(JWT_SECRET));
    }
}

 

아래 테스트를 해보면 실패한다. 

static 메서드는 메모리에 올라갈 때 생성되기 때문에

아래와 같이 런타임에 mock을 하면 안 된다. 

@ExtendWith(MockitoExtension.class)
public class UserControllerTest 

    @Mock
    private JwtUtil jwtUtil;

    @Test
    @DisplayName("로그인 컨트롤러 성공 테스트")
    void loginSuccessTest() throws Exception {
        given(jwtUtil.createToken(1L, "aaa@naver.com"))
            .willReturn("token");
    }
}

 

 

해결 

mockito-inline 사용하기

기존에는 mock 객체를 만들기 위해서 SubclassByteBuddyMockMaker를 사용했고

SubclassByteBuddyMockMaker는 상속을 사용해서 mock 객체를 만들었다.

 

mockito-inline을 의존성으로 등록하면 mock 객체를 만들때 InlineByteBuddyMockMaker를 사용하게 된다.

InlineByteBuddyMockMaker는 바이트코드 조작을 통해 mock 객체를 만들어주는데

이를 사용하면 static 메서드를 mocking 할 수 있다.

@ExtendWith(MockitoExtension.class)
public class UserControllerTest 

    @BeforeAll
    public static void beforeAll() {
        mockStatic(JwtUtil.class);
    }

    @Test
    @DisplayName("로그인 컨트롤러 성공 테스트")
    void loginSuccessTest() throws Exception {
        given(JwtUtil.createToken(1L, "aaa@naver.com"))
            .willReturn("token");
    }
}

Bean으로 등록하기

static 메서드를 사용하지 말고 빈으로 등록해서 사용해서 해결할 수도 있다.

@Component
public class TokenProvider {

    public void verifyToken(String jwtToken) {
        JWT.require(Algorithm.HMAC512(JWT_SECRET)).build().verify(jwtToken);
    }

    public String createToken(Long userId, String email)  {
        return JWT.create()
                .withClaim("id", userId)
                .sign(Algorithm.HMAC512(JWT_SECRET));
    }
}

 

 

참고

mockito v.5.0.0부터 mock 객체를 만들때

기본적으로 InlineByteBuddyMockMaker를 사용하는 것으로 바뀌었기 때문에

mockito-inline을 추가적으로 의존할 필요가 없어졌다.