
๐งช 1. Mock / Stub / Mockito / @MockBean ์ ๋ฆฌ
์ค์ ์ฝ๋๋ฅผ ๊ทธ๋๋ก ํ ์คํธ์ ์ฌ์ฉํ๋ฉด ์ด๋ฐ ๋ฌธ์ ๊ฐ ์๊ธด๋ค.
๊ทธ๋์ ํ ์คํธ์์๋ โ์ง์ง ๊ฐ์ฒด ๋์ ์ฌ์ฉํ๋ ๊ฐ์ง ๊ฐ์ฒดโ๋ฅผ ์ฐ๋๋ฐ, ์ด๊ฒ๋ค์ ํตํ์ด ํ ์คํธ ๋๋ธ(Test Double) ์ด๋ผ๊ณ ํ๋ค.
๋ํ์ ์ธ ์ข ๋ฅ:
์ด ์ค์์ Spring + Mockito ํ๊ฒฝ์์ ๊ฐ์ฅ ์์ฃผ ๋ณด๋ ๊ฑด Stub, Mock ์ด๋ค.
โ๋ฏธ๋ฆฌ ์ ํด๋ ๋ฐํ๊ฐ๋ง ๋๋ ค์ฃผ๋ ๋จ์ ๊ฐ์ง ๊ฐ์ฒดโ
ํน์ง:
public interface UserService {
User findById(Long id);
}
public class StubUserService implements UserService {
@Override
public User findById(Long id) {
// ํญ์ ๊ฐ์ ๊ฐ ๋ฐํ (ํ
์คํธ์ฉ)
return new User("pong", 27);
}
}
ํ ์คํธ์์:
UserService userService = new StubUserService();
User user = userService.findById(1L);
// ํญ์ "pong" ๋ฐํ
์ค๋ฌด์์ Stub ํด๋์ค๋ฅผ ์ง์ ๋ง๋๋ ๊ฒฝ์ฐ๋ ์ ์ ์ค์ด๋๋ ์ถ์ธ๋ค.
๋๋ถ๋ถ Mock์ ์ฌ์ฉํ๋ฉด์ Stub ๊ธฐ๋ฅ๊น์ง ํจ๊ป ์ฐ๊ธฐ ๋๋ฌธ์ด๋ค.
โ๊ฐ์ง ๊ฐ์ฒด์ธ๋ฐ,
- ์ด๋ค ๊ฐ์ ๋ฐํํ๊ฒ ๋ง๋ค ์๋ ์๊ณ
- ์ด๋ป๊ฒ ํธ์ถ๋์๋์ง(ํ์, ํ๋ผ๋ฏธํฐ)๋ฅผ ๊ฒ์ฆํ ์ ์๋ ๊ฐ์ฒดโ
Mock์ Stub๋ณด๋ค ํจ์ฌ ๊ฐ๋ ฅํ๋ค.
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository; // ๊ฐ์ง Repo
@InjectMocks
private UserServiceImpl userService; // ํ
์คํธํ ์ค์ Service
@Test
void findById_์ ์์กฐํ() {
// given
User user = new User("pong", 27);
when(userRepository.findById(1L))
.thenReturn(Optional.of(user)); // Stub ์ญํ
// when
User result = userService.findById(1L);
// then
assertThat(result.getName()).isEqualTo("pong");
verify(userRepository, times(1)).findById(1L); // Mock์ ํ๋ ๊ฒ์ฆ
}
}
์ฌ๊ธฐ์ ์ค์ํ ํฌ์ธํธ:
when(...).thenReturn(...)verify(..., times(1))thenThrow)verify(mock).save(argThat(...)))never())Stub์ผ๋ก๋ ํ๋ ๊ฒ์ฆ์ด ๋ถ๊ฐ๋ฅํ์ง๋ง,
Mock์ ํ๋๊น์ง ๊ฒ์ฆํ ์ ์๊ธฐ ๋๋ฌธ์ ํจ์ฌ ์ ์ฐํ๋ค.
โJava์์ Mock ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ ,
๊ทธ ๋์์ ์ค์ ํ๊ณ ,
ํธ์ถ ์ฌ๋ถ๋ฅผ ๊ฒ์ฆํ ์ ์๊ฒ ํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌโ
Spring Boot ํ ์คํธ์์ ๊ฑฐ์ ๊ธฐ๋ณธ์ฒ๋ผ ์ฌ์ฉํ๋ค.
์ฃผ์ ๊ธฐ๋ฅ:
@Mock, @InjectMocks ์ง์when(...).thenReturn(...), thenThrow(...) ๋ฑ Stub ์ค์ verify(...) ๋ก ํธ์ถ ์ฌ๋ถ / ํ์ / ํ๋ผ๋ฏธํฐ ๊ฒ์ฆspy๋ก ์ค์ ๊ฐ์ฒด + ์ผ๋ถ๋ง ๋ชจํน๋ ๊ฐ๋ฅโSpring ApplicationContext์ ์๋ Bean์ Mockito Mock ๊ฐ์ฒด๋ก ๊ต์ฒดํด์ฃผ๋ Spring Boot ํ ์คํธ์ฉ ์ด๋ ธํ ์ด์ โ
์:
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService; // ์ฌ๊ธฐ!
@Test
void getUser() throws Exception {
when(userService.findById(1L))
.thenReturn(new User("pong", 27));
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("pong"));
}
}
์ด ์ฝ๋์์:
UserService ๊ตฌํ์ฒด๋ฅผ Bean์ผ๋ก ๋ฑ๋กํด์ผ ํจ@WebMvcTest๋ Service๋ฅผ ๋ก๋ํ์ง ์์@MockBean์ด ํด๋น ํ์
์ ๋น์ Mockito Mock์ผ๋ก ๋ฑ๋ก@WebMvcTest๋ Controller + MVC ๊ด๋ จ Bean๋ง ๋ก๋ํ๋ค.null์ด ๋จ.NullPointerException ๋ฐ์.| ์ด๋ ธํ ์ด์ | ์ด๋์ ์ฐ๋๊ฐ | ์ญํ |
|---|---|---|
@Mock | ์์ JUnit + Mockito ํ ์คํธ (Spring context ์์) | ๋จ์ํ Mockito mock ์์ฑ |
@MockBean | Spring Boot ํ ์คํธ (Spring context ์ฌ์ฉ) | ํด๋น ํ์ ์ Spring Bean์ Mockito mock์ผ๋ก ๊ต์ฒด |
์ ๋ฆฌํ๋ฉด:
Stub
Mock
ํ๋ Spring ํ๊ฒฝ์์๋ ์ง์ Stub ํด๋์ค๋ฅผ ๋ง๋๋ ๊ฒ๋ณด๋ค, Mockito Mock์ ์จ์ Stub+Mock ๊ธฐ๋ฅ์ ๊ฐ์ด ์ฌ์ฉํ๋ ํจํด์ด ์๋์ ์ผ๋ก ๋ง๋ค.
๐งต ์์ฝ
๐ฐ๏ธ 2. MockMvc / @WebMvcTest๋ก ์ปจํธ๋กค๋ฌ ํ ์คํธ ์ ๋๋ก ์ดํดํ๊ธฐ
Spring์์ ์ปจํธ๋กค๋ฌ๋ฅผ ํ ์คํธํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ 3๊ฐ์ง ๋ ๋ฒจ์ด ์๋ค.
๋จ์ ํ ์คํธ ์์ค
์น ๋ ์ด์ด ํ ์คํธ
ํตํฉ ํ ์คํธ
@SpringBootTest + TestRestTemplate ๋๋ WebTestClient์ฌ๊ธฐ์ ์ฐ๋ฆฌ๊ฐ ๋ณด๊ณ ์๋ ๊ฑด 2๋ฒ: MockMvc + @WebMvcTest ์กฐํฉ์ด๋ค.
โSpring MVC์ DispatcherServlet์ ์ค์ ๋ก ๋์์ํค๋ฉด์,
HTTP ์์ฒญ/์๋ต ํ๋ฆ์ WAS ์์ด ํ ์คํธํ ์ ์๊ฒ ํด์ฃผ๋ ๋๊ตฌโ
์ฆ:
get("/path"), post("/path") ๊ฐ์ ์์ฒญ์ ๋ณด๋ผ ์ ์๊ณ @WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void ์ ์ _๋จ๊ฑด_์กฐํ_API() throws Exception {
// given
when(userService.findById(1L))
.thenReturn(new User("pong", 27));
// when & then
mockMvc.perform(get("/api/users/{id}", 1L)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("pong"))
.andExpect(jsonPath("$.age").value(27));
}
}
์ฌ๊ธฐ์ MockMvc๋ ๋ค์์ ์ํํ๋ค:
GET /api/users/1 ์์ฒญ ์์ฑstatus, jsonPath๋ก ์๋ต ๊ฒ์ฆโController์ MVC ๊ด๋ จ ๊ตฌ์ฑ ์์๋ง ๋ก๋ฉํด์ ์น ๋ ์ด์ด๋ฅผ ์๊ฒ ํ ์คํธํ๋๋ก ๋ง๋ Spring Boot ํ ์คํธ์ฉ ์ ๋ ธํ ์ด์ โ
์:
@WebMvcTest(UserController.class)
class UserControllerTest {
...
}
๋ก๋๋จ:
@Controller, @RestController@ControllerAdvice (์ ์ญ ์์ธ ์ฒ๋ฆฌ ๋ฑ)WebMvcConfigurer ๋ฑ ์ผ๋ถ)ObjectMapper (JSON ์ง๋ ฌํ/์ญ์ง๋ ฌํ)๋ก๋๋์ง ์์:
@Service@Repository@Component๊ทธ๋์ @WebMvcTest๋ ์ค์ง โ์น ๋ ์ด์ด ํ ์คํธโ ์ฉ์ด๋ผ๊ณ ๋ณด๋ฉด ๋๋ค.
์ ์ฒด ๊ทธ๋ฆผ์ ์ด๋ ๊ฒ ๋๋ค:
@WebMvcTest(UserController.class)
โ
Spring์ด UserController, MVC ๊ด๋ จ ๋น๋ง ๋ก๋
โ
@MockBean UserService
โ
์ค์ UserService ๋น ๋์ Mockito Mock ๊ฐ์ฒด๋ฅผ Spring Bean์ผ๋ก ๋ฑ๋ก
โ
@Autowired MockMvc
โ
MockMvc๋ก HTTP ์์ฒญ ์๋ฎฌ๋ ์ด์
โ
DispatcherServlet โ Filter โ Interceptor โ UserController โ Mock(UserService)
โ
์๋ต(JSON) ๊ฒ์ฆ
์ด ๊ตฌ์กฐ ๋๋ถ์:
๋ชฉ์ : ์ปจํธ๋กค๋ฌ + ์น ๋ ์ด์ด ๊ฒ์ฆ
์ฅ์ :
๋จ์ :
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerIntegrationTest {
@Autowired
private MockMvc mockMvc;
// ์ง์ง UserService, UserRepository, DB๊น์ง ์ ๋ถ ๋ก๋
}
๋ชฉ์ : ์กฐ๊ธ ๋ ๋ฌด๊ฑฐ์ด ํตํฉ ํ ์คํธ
์ฅ์ :
๋จ์ :
| ์ํฉ | ์ถ์ฒ ๋ฐฉ์ |
|---|---|
| ์ปจํธ๋กค๋ฌ์ Request/Response, Validation, Security, ์์ธ ์ฒ๋ฆฌ๋ง ๊ฒ์ฆํ๊ณ ์ถ๋ค | @WebMvcTest + MockMvc + @MockBean |
| ์๋น์ค ๋ก์ง + DB๊น์ง ์ค์ ๋ก ๋์์ํค๋ฉฐ API๋ฅผ ๊ฒ์ฆํ๊ณ ์ถ๋ค | @SpringBootTest + MockMvc |
| ์ค์ ์ด์๊ณผ ๊ฑฐ์ ๋์ผํ ํ๊ฒฝ์์ E2E ๋ ๋ฒจ ํ ์คํธ๋ฅผ ํ๊ณ ์ถ๋ค | @SpringBootTest + TestRestTemplate/WebTestClient |
ํ์ค์ ์ธ ํจํด:
@WebMvcTest + MockMvc + @MockBean@SpringBootTest (๋๋ slice ํ
์คํธ)@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean
UserService userService;
@Test
void getUser_์ ์_์๋ต() throws Exception {
// given
when(userService.findById(1L))
.thenReturn(new User("pong", 27));
// when & then
mockMvc.perform(get("/api/users/{id}", 1L)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("pong"))
.andExpect(jsonPath("$.age").value(27));
verify(userService, times(1)).findById(1L);
}
}
์ด ํ ํ์ผ์ ์ง๊ธ๊น์ง ์ ๋ฆฌํ ๊ฐ๋ ์ด ์ ๋ถ ๋ค์ด ์๋ค:
@WebMvcTest โ ์น ๋ ์ด์ด๋ง ๋ก๋MockMvc โ HTTP ์์ฒญ/์๋ต ํ
์คํธ@MockBean โ Service๋ฅผ Mockito Mock์ผ๋ก ๊ต์ฒดwhen(...).thenReturn(...) โ Stub ๊ธฐ๋ฅverify(...) โ Mock ํ๋ ๊ฒ์ฆ๐งต ์์ฝ
@WebMvcTest + MockMvc + @MockBean ์กฐํฉ์ผ๋ก