본문으로 바로가기

@MockBean vs @Mock,@InjectMocks

category SPRING/스프링부트 2025. 1. 6. 15:26
728x90
반응형
SMALL

@MockBean vs @Mock,@InjectMocks

refreshAccressToken(..) 메서드와 createAccessToken(..) 메서드의 결과값이 같은지 확인하고 싶어 테스트 코드를 작성하였다.

@RequiredArgsConstructor
@Slf4j
@Service
public class JwtService {

    private final UserService userService;    

    public String refreshAccressToken(String token){
        validateToken(token);
        Account find = userService.find(token);
        return createAccessToken(find.getUserId(), find.getName());
    }

    public String createAccessToken(String userEmail, String name){
        Claims claims = Jwts.claims()
                .add("name", name)
                .add("email", userEmail)
                .add("auth", RoleUser.ADMIN.getValue())
                .build();

        Date now = new Date();

        String token = Jwts.builder()
                .subject(ACCESS_TOKEN_SUBJECT)
                .claims(claims)
                .issuedAt(now)
                .expiration(new Date(now.getTime() + TOKEN_EXPIRE_TIME*3600)) //1시간
                .signWith(getSignKey())
                .compact();

        return token;
    }

Test Code

@SpringBootTest
public class JwtServiceTests {

    @Mock
    private UserService userService;  // userService를 mock합니다.

    @InjectMocks
    private JwtService jwtService;  // 테스트할 TokenService

    @BeforeEach
    public void setUp() {
        MockitoAnnotations.openMocks(this);  // Mock 객체 초기화
    }

    @Test
    public void tokenDiffTest(){
        //given
        String email = "jyyoon@gmail.com";
        String name = "윤주영";
        String createToken = jwtService.createAccessToken(email, name);

        Account account = Account.builder()
                .userId(email)
                .name(name)
                .role(RoleUser.USER)
                .provider("네이버")
                .build();

        when(userService.find(createToken)).thenReturn(account);

        String refreshToken = jwtService.refreshAccressToken(createToken);
           ...

    }
}

테스트 하려던 메서드들은 JwtService 클래스의 메서드이고, refreshAccressToken(..) 메서드에서는 db조회하는 userService.find(token) 코드가 있었다.

나는 테스트시, 직접 db조회가 되는것이 싫었기 때문에 가상으로 만들어주고 싶어서 @Mock 을 사용하여 UserService를 mocking 해주었다.

userService.find(token) 결과 값은 가상으로 when(userService.find(createToken)).thenReturn(account) 만들도록 when을 사용했다.

그러나 db조회 코드가 내가 원하는 값으로 mocking 되지 않고 null로 되었다.


왜 내가 원하는 값으로 mocking되지 않았지?????


조사결과 아래와 같은 차이점이 있다.


1. @Mock

  • 역할:
    • Mockito의 Mock 객체를 생성합니다.
    • 테스트에서 특정 객체의 동작을 Mocking하여 제어할 수 있습니다.
    • Spring Context에 영향을 미치지 않습니다.
  • 사용 환경:
    • Spring Context가 필요하지 않은 단위 테스트(Unit Test)에서 주로 사용됩니다.


2. @InjectMocks

  • 역할:
    • 테스트 대상 클래스에 @Mock으로 선언된 Mock 객체를 자동으로 주입합니다.
    • 의존성이 있는 실제 객체 대신 Mock 객체를 주입하여 테스트 환경을 설정합니다.
  • 사용 환경:
    • Mock 객체가 여러 개 필요하고, 이 객체들을 테스트 대상 클래스에 주입해야 하는 경우 사용됩니다.


3. @MockBean

  • 역할:
    • Spring Context에 Mock 객체를 주입합니다.
    • 테스트 대상이 실제 Spring Context와 연동되어야 하는 경우(예: @Service, @Repository) 사용됩니다.
  • 사용 환경:
    • Spring Boot 통합 테스트(Integration Test)에서 사용됩니다.
    • @SpringBootTest, @WebMvcTest, @DataJpaTest 등과 함께 사용됩니다.
    • Spring Context 내의 Bean을 Mocking해야 할 때 적합합니다.

정리

언제 사용해야 할까?

Spring Context가 필요 없는 단위 테스트:

  • @Mock@InjectMocks를 사용합니다.
  • Spring Context 로딩 없이 테스트를 빠르게 수행할 수 있습니다.

Spring Context가 필요한 통합 테스트:

  • @MockBean을 사용합니다.
  • Spring 빈 간의 의존성을 유지하면서 특정 빈만 Mocking할 수 있습니다.

@Mock@InjectMocks@SpringBootTest 와 혼용하여 사용하지 않도록 주의하자.

@InjectMocksMockito가 생성하고 관리한다.

Spring과 무관한 단위 테스트에 적합하며, Spring Context를 사용하지 않습니다.

Spring Context와 관련된 테스트를 하려면 @MockBean을 사용하는 것이 적합하다.

@InjectMocks 주입 예시

@ExtendWith(MockitoExtension.class)
public class ExampleTest {

    @Mock
    private UserService userService; // Mockito가 관리

    @InjectMocks
    private UserController userController; // Mockito가 관리

    @Test
    public void testExample() {
        when(userService.getUserName(1L)).thenReturn("윤주영");

        String result = userController.getUserName(1L);

        assertEquals("윤주영", result); // 정상 작동
    }
}

그래서 어떻게 수정함?

@SpringBootTest
public class JwtServiceTests {

    @MockBean
    private UserService userService;  // userService를 mock합니다.

    @Autowired
    private JwtService jwtService;  // 테스트할 TokenService

    @Test
    public void tokenDiffTest(){
        //given
        String email = "jyyoon@gmail.com";
        String name = "윤주영";
        String createToken = jwtService.createAccessToken(email, name);

        Account account = Account.builder()
                .userId(email)
                .name(name)
                .role(RoleUser.USER)
                .provider("네이버")
                .build();

        when(userService.find(createToken)).thenReturn(account);

        String refreshToken = jwtService.refreshAccressToken(createToken);
        assertNotNull(refreshToken);  // 결과 토큰이 null이 아님을 확인
        ...
        ...

    }
}
728x90
반응형
LIST