ํฐ์คํ ๋ฆฌ ๋ทฐ
[๋ฆฌํฉํ ๋ง] JUNIT5 ํ ์คํธ ์ฝ๋ ๋ฆฌํฉํ ๋งํ๊ธฐ - @ParameterizedTest ์ @MethodSource ํ์ฉ
Nickolodeon 2023. 1. 5. 22:42๐ ๋ฆฌํฉํ ๋ง ์ ์ ์ฝ๋
ํ๋ก์ ํธ ์ค ํ
์คํธ๋ฅผ ์์ฑํ๋ฉด์, ๊ธฐ๊ณ์ ์ด๊ณ ๋ฐ๋ณต์ ์ผ๋ก ํ๋์ ํจํด์ ์ฌ๋ฌ ์ํฉ๋ณ ํ
์คํธ ๋ฉ์๋์ ๊ณตํต์ผ๋ก ์ฌ์ฉํ๊ณ ์์์ ๋ฐ๊ฒฌํ๋ค. ์๋ ๋ ์ฝ๋ ๋ธ๋ญ๋ค๊ณผ ๊ฐ์ด ๋ชจ๋ ์ฝ๋๊ฐ ๋์ผํ๊ณ ErrorCode
์ ์ข
๋ฅ์ HTTP ์ํ๋ฅผ ๋ํ๋ด๋ status()
๋ง ์ฐจ์ด๊ฐ ์๋ ๋ฉ์๋๋ค์ด ์์๋ค. ๊ธฐ๋ฅ ๊ตฌํ ๋ฉ์๋๋ค์ ์์ฑํ ๋์๋ ํ
ํ๋ฆฟ ์ฝ๋ฐฑ ํจํด๊ณผ ๊ฐ์ด ์ธํฐํ์ด์ค๋ฅผ ํ์ฉํด ์ค๋ณต์ ์ ๊ฑฐํ๊ณ ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ ์ ์์๋๋ฐ, ํ
์คํธ ์ฝ๋์์๋ ํ ๋ฒ๋ ๋งค๊ฐ ๋ณ์๋ฅผ ๊ฐ์ง ๋ฉ์๋๋ฅผ ์ ์ํด๋ณธ ์ ์ด ์๋ค๋ ๋จ์ํ ์ด์ ๋ก ํ๋์ ๋ฉ์๋์์ ์์ฑํ ์ฝ๋๋ฅผ ctrl+c
, ctrl+v
(๋ณต์ฌ ํ ๋ถ์ฌ๋ฃ๊ธฐ) ํ์ฌ ๋ค๋ฅธ ๋ฉ์๋์์ ์ฐ๋ ๋นํจ์จ์ ์ธ ํ๋ก๊ทธ๋๋ฐ์ ํ๋ค. ๋ฆฌํฉํ ๋ง์ ๊ฑฐ์ณ์ ์ด๋ป๊ฒ ์ค๋ณต์ ์ค์ด๊ณ ๊ณตํต๋ ํจํด์ ๋ถ๋ฆฌํ๋์ง ์์๋ณด์.
@Test
@DisplayName("๋ก๊ทธ์ธ ํ์ง ์์๊ฑฐ๋ ์์ฑ์์ ํ์ฌ ๋ก๊ทธ์ธ๋ ์ ์ ๊ฐ ์ผ์นํ์ง ์์ ํฌ์คํธ ์์ ์ ์คํจํ๋ค.")
@WithMockUser
void fail_edit_post_inconsistent_user() throws Exception {
given(postService.editPost(any(), eq(postId), any()))
.willThrow(new UserException(ErrorCode.INVALID_PERMISSION, "์ฌ์ฉ์๊ฐ ๊ถํ์ด ์์ต๋๋ค."));
mockMvc.perform(put(editUrl).contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(editPostRequest))
.with(csrf()))
.andExpect(status().isUnauthorized())
.andExpect(jsonPath("$.result.errorCode").value("INVALID_PERMISSION"))
.andDo(print());
verify(postService).editPost(any(), eq(postId), any());
}
@Test
@DisplayName("๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์์ ํ ํฌ์คํธ๋ฅผ ์ฐพ์ง ๋ชปํ๋ฉด ์์ ์ ์คํจํ๋ค.")
@WithMockUser
void fail_edit_post_not_in_db() throws Exception {
given(postService.editPost(any(), eq(postId), any()))
.willThrow(new UserException(ErrorCode.DATABASE_ERROR, "DB ์๋ฌ"));
mockMvc.perform(put(editUrl).contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(editPostRequest))
.with(csrf()))
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.result.errorCode").value("DATABASE_ERROR"))
.andDo(print());
verify(postService).editPost(any(), eq(postId), any());
}
๐จ @ParameterizedTest
์ @MethodSource
๋ฅผ ํ์ฉํ ๋ฆฌํฉํ ๋ง
์ฐ์ ์ฌ์ฉํ ์ด๋ ธํ ์ด์ ์ ๋ค์๊ณผ ๊ฐ๋ค:
@ParameterizedTest
: ๋งค๊ฐ ๋ณ์๊ฐ ์๋ ํ ์คํธ ๋ฉ์๋์ ๋ถ์ด๋ ์ด๋ ธํ ์ด์ .@MethodSource
:@ParameterizedTest
๋ ๋ค์ํ Source (์ถ์ฒ) ๋ก๋ถํฐ ๋ฉ์๋์ ์ฌ์ฉ๋๋ ๋งค๊ฐ๋ณ์๋ฅผ ๋ถ๋ฌ์จ๋ค.@EnumSource
: enum ํ์ ํด๋์ค์ ์์ ๋ณ์๋ค์ ๋งค๊ฐ ๋ณ์๋ก ํ์ฉํ๊ณ ์ ํ ๋ ์ฌ์ฉ๋๋ ์ด๋ ธํ ์ด์ .@NullSource
,@EmptySource
: ๊ฐ๊ฐ null ๊ฐ, ๋น ๊ฐ (๋ฌธ์์ด, Collection, ๋ฐฐ์ด๋ง ํด๋น) ์ ํ์ฉํ๊ณ ์ ํ ๋ ์ฌ์ฉ.@CsvSource
: ์ผํ๋ก ๋ถ๋ฆฌ๋ ๊ฐ์ ์จ์ ์ฌ๋ฌ ๋งค๊ฐ ๋ณ์๋ฅผ ์ง์ ํ๋ ์ด๋ ธํ ์ด์ .@ValueSource
: ํ๋์ ๋งค๊ฐ ๋ณ์๋ฅผ ์ง์ ํ ๋ ์ฌ์ฉํ๋ ์ด๋ ธํ ์ด์ .@MethodSource
๋ ๋ณต์กํ ๊ฐ์ฒด๋ค์ด ๋งค๊ฐ ๋ณ์์ผ ๋, ๋งค๊ฐ ๋ณ์๋ฅผ ์ง์ ํ๋ ๋ฉ์๋ ํ๋๋ฅผ ์์ฑํ์ฌ ํ์ฉํ๋ ์ด๋ ธํ ์ด์ ์ด๋ค.
- ๊ฐ๋จํ ์๊ฐํ๋ฉด,
@MethodSource
์ธ์๋ ์๋์ ๊ฐ์ Source ๋ค์ด ์๋ค:
๊ทธ๋์, ์๋์ ๊ฐ์ด ๋ฉ์๋ ์์ ๋ ๊ฐ์ ์ด๋
ธํ
์ด์
์ ๋ถ์ด๊ณ , @MethodSource
์๋ ์ด๋ค ๋ฉ์๋๋ฅผ ์ฐธ์กฐํ ๊ฒ์ธ์ง ๋ช
์ํ๋ค.
@ParameterizedTest
@DisplayName("๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์์ ํ ํฌ์คํธ๋ฅผ ์ฐพ์ง ๋ชปํ๋ฉด ์์ ์ ์คํจํ๋ค.")
@WithMockUser
@MethodSource("provideErrorCase")
void fail_edit_post_not_in_db(ErrorCode errorCode, ResultMatcher isError) throws Exception {
given(postService.editPost(any(), eq(postId), any()))
.willThrow(new UserException(errorCode, errorCode.getMessage()));
mockMvc.perform(put(editUrl).contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(editPostRequest))
.with(csrf()))
.andExpect(isError)
.andExpect(jsonPath("$.result.errorCode").value(errorCode.name()))
.andDo(print());
verify(postService).editPost(any(), eq(postId), any());
}
@MethodSource
์์ ์ฌ์ฉํ ๋ฉ์๋๋ ์๋์ ๊ฐ๋ค:
private static Stream<Arguments> provideErrorCase() {
return Stream.of(Arguments.of(ErrorCode.INVALID_PERMISSION, status().isUnauthorized()),
Arguments.of(ErrorCode.DATABASE_ERROR, status().isInternalServerError()));
}
๋ฐํ ํ์
์ด Stream<Arguments>
๋ก ๋์ด ์๋ค. provideErrorCase()
๋ Stream
ํํ๋ก ๋งค๊ฐ ๋ณ์, Arguments
๋ค์ ๋ฌถ์ด์ ๋ฐํํ๋ค. ์์ ์ค๋ณต๋ ํจํด์ ๊ฐ์ง๊ณ ์๋ ๋ ์ฝ๋๋ ๊ฐ๊ฐ INVALID_PERMISSION
์๋ฌ์ DATABASE_ERROR
์๋ฌ์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ํ
์คํธํ๋ ์ฝ๋์๋ค. ๋งค๊ฐ ๋ณ์๋ก ์ฌ์ฉ๋ ErrorCode
์ status()
๋ฅผ Stream
ํํ๋ก ๋ฌถ์ด์ ์ง์ ํด์ค ๊ฒ์ ๋ณผ ์ ์๋ค.
๐งถ ๋ฌธ์ ๋ฐ์๊ณผ ํด๊ฒฐ
์ฌ๋ฌ ๋ฉ์๋๋ฅผ ํ๋๋ก ๋ฌถ๋ค ๋ณด๋ฉด, ์ปจํธ๋กค๋ฌ ํ
์คํธ์ ํน์ฑ ์ ํ๋์ API ์์๋ Mock ๊ฐ์ฒด Service ๋ฅผ ์ฌ์ฉํ์ง๋ง, ๋ค๋ฅธ ํ๋์ API ์์๋ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ๊ฐ ์์์๋ ํจํด์ด ๊ฒน์น๋ ์ด์ ๋ก ๋ฌถ๋ ์ํฉ์ด ๋ฐ์ํ๋ค. ์ฆ, ํ
์คํธ ๋์ API ์์ ์ฌ์ฉ๋์ง ์์ผ๋ฉด์ ๋์์ด ์ ์๋ ๊ฐ์ฒด๊ฐ ์๋ ๊ฒ์ด๋ค. ์ด ๋์๋ unnecessary stubbing
์๋ฌ๊ฐ ๋ฐ์ํ๋ค. ํด๊ฒฐ์ ์ํด ๊ตฌ๊ธ๋ง์ ํ ๊ฒฐ๊ณผ, ๋ ๊ฐ์ง์ ๋ฐฉ๋ฒ์ด ์์์ ์๊ฒ ๋์๋ค. ํ๋๋ Mockito
์ ์คํํฑ ๋ฉ์๋๋ฅผ ํ์ฉํ๋ ๊ฒ์ด๊ณ , ๋ค๋ฅธ ํ๋๋ ์ด๋
ธํ
์ด์
์ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค. ๋๋ ์ด๋ค ํน์ ์ํฉ์์ ๋ถํ์ํ๊ฒ Mock
๊ฐ์ฒด์ ๋์์ ์ ์ (stubbing
) ์ ํ๊ณ ์๋์ง ์๊ณ ์์ด์, ์๊ณ ์๋ ์์น์์ ๋์์ ์ ์ํ๋ ์ค์ ๋ฉ์๋๋ฅผ ์ถ๊ฐํ์๋ค:
Mockito.lenient().when(postRepository.findById(mockPost.getId())).thenReturn(optionalPost);
Mockito.lenient().when(userRepository.findByUserName(mockPost.getAuthor().getUserName())).thenReturn(optionalUser);
๐ก ํ ๋ฒ ๋ ๋ฆฌํฉํ ๋ง: enum
ํ์
ํด๋์ค ํ์ฉ
์ดํ ๋ง์ ์์ ๋ณ์๋ค์ด ํ๋ ์ฝ๋ฉ ๋ ๋ชจ์ต์ด ์ง์ ๋ถํด์ PostTestEssentials
enum
ํ์
ํด๋์ค๋ฅผ ์์ฑํ๋ค. ๋ค์ํ ์์ ๋ณ์๋ค์ ํด๋์ค ๋ด๋ถ์ ์ถ๊ฐํ ๋ค, ๋กฌ๋ณต์ ์ฌ์ฉํด ๊ฐ ๋ณ์์ ๊ฐ์ @Getter
๋ฉ์๋๋ฅผ ํ์ฉํด ๊ฐ์ ธ์ค๊ฒ ํ๋ค.
+ ๋กฌ๋ณต์ test
๋๋ ํ ๋ฆฌ ํ์ ํด๋์ค๋ค์์๋ ์ฌ์ฉํ ์ ์๋๋ก build.gradle
์ ์์กด์ฑ์ ์ถ๊ฐํด์ฃผ์๋ค:
๐ ์ถ์ฒ
https://stackoverflow.com/questions/42947613/how-to-resolve-unneccessary-stubbing-exception
How to resolve Unneccessary Stubbing exception
My Code is as below, @RunWith(MockitoJUnitRunner.class) public class MyClass { private static final String code ="Test"; @Mock private MyClassDAO dao; @InjectMocks private
stackoverflow.com
'Project' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Refactoring] ์บ์ฑ ์ฌ์ฉํด ์ฟผ๋ฆฌ ์คํ ์๋ ๊ฐ์ ํ๊ธฐ (1) | 2023.03.05 |
---|---|
[Refactoring] ์ ๋ต ํจํด ์ ์ฉํ๊ธฐ (1) (0) | 2023.02.22 |
[Spring Boot] Could not load or find main class ์๋ฌ (0) | 2023.02.22 |
[๊ฐ๋ฐ์ผ์ง] Spotify Web API ์ฌ์ฉ๊ธฐ (1) (0) | 2023.01.31 |
[Git] Merge ๋ฅผ revert ํ ๋ค์ ์ด๋ฅผ revert ํ๊ธฐ (2) | 2023.01.03 |
- Total
- Today
- Yesterday
- docker
- ๊ฐ์ ์๋ฒ
- Spring Boot
- DTO
- ์๊ณ ๋ฆฌ์ฆ
- LazyInitializationException
- json web token
- google cloud
- ๊น๋ฉ
- @RequestBody
- FCM
- ์ค์๊ฐ๋ฐ์ดํฐ
- N+1
- Jackson
- ์ธ์ฆ/์ธ๊ฐ
- ๋์ปค
- ํ๋ก๊ทธ๋๋จธ์ค
- ์ง์ฐ ๋ก๋ฉ
- Java Data Types
- JOIN FETCH
- spring
- ๊ธฐ์ง๊ตญ ์ค์น
- JPA
- Firebase
- ci/cd
- ์ญ์ง๋ ฌํ
- gitlab
- DeSerialization
- JPQL
- ์ฝํ
์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |