ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๐Ÿ” ๋ฆฌํŒฉํ† ๋ง ์ „์˜ ์ฝ”๋“œ

ํ”„๋กœ์ ํŠธ ์ค‘ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ, ๊ธฐ๊ณ„์ ์ด๊ณ  ๋ฐ˜๋ณต์ ์œผ๋กœ ํ•˜๋‚˜์˜ ํŒจํ„ด์„ ์—ฌ๋Ÿฌ ์ƒํ™ฉ๋ณ„ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ์— ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Œ์„ ๋ฐœ๊ฒฌํ–ˆ๋‹ค. ์•„๋ž˜ ๋‘ ์ฝ”๋“œ ๋ธ”๋Ÿญ๋“ค๊ณผ ๊ฐ™์ด ๋ชจ๋“  ์ฝ”๋“œ๊ฐ€ ๋™์ผํ•˜๊ณ  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

https://www.baeldung.com/parameterized-tests-junit-5

๊ณต์ง€์‚ฌํ•ญ
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday
๋งํฌ
ยซ   2025/02   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
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
๊ธ€ ๋ณด๊ด€ํ•จ