๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿš€ ์šฐ์•„ํ•œํ…Œํฌ์ฝ”์Šค 6๊ธฐ ์ง€์› ๊ธฐ๋ก

[ํ”„๋ฆฌ์ฝ”์Šค] @Test์—์„œ ์ธ์ž๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์™œ ParameterResolutionException์ด ๋ฐœ์ƒํ• ๊นŒ?

by dev_writer 2023. 11. 5.

์„œ๋ก 

์šฐ์•„ํ•œ ํ…Œํฌ์ฝ”์Šค ํ”„๋ฆฌ์ฝ”์Šค ๋ฏธ์…˜์„ ํ•˜๋ฉด์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋˜ ๋„์ค‘, ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค. (์ตœ์ข… ์ฝ”๋“œ๋Š” ๊ฒ€์ฆ ๋กœ์ง์„ ๋‹ค๋ฅด๊ฒŒ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ง€๊ธˆ์€ ์ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์•„์˜ˆ ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.)

@Test
@ParameterizedTest
@ValueSource(strings = {"ๅด”ใฏะปู†", "?-bob"})
void ํ•œ๊ธ€๊ณผ_์•ŒํŒŒ๋ฒณ_์ˆซ์ž๋ฅผ_์ œ์™ธํ•œ_๋ชจ๋“ _๋ฌธ์ž๋Š”_์•ˆ๋œ๋‹ค(final String value) {
    // given & when
    Throwable exception = Assertions.assertThrows(IllegalArgumentException.class, () -> {
        Name name = Name.from(value);
    });
    // then
    assertThat(exception.getMessage()).isEqualTo(CAR_NAMV_VALUE_EXCEPTION.toString());
}

 

์ด๋ ‡๊ฒŒ ํ•˜๋‹ˆ, ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

์™œ ์ด๋Ÿฐ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒ๋œ ๊ฒƒ์ผ๊นŒ์š”? ์ง€๊ธˆ๋ถ€ํ„ฐ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


1. @Test์™€ @ParameterizedTest๋Š” ํ•จ๊ป˜ ์“ฐ๋ฉด ์•ˆ ๋œ๋‹ค.

์œ„์˜ ์‚ฌ์ง„์„ ๋ณด๋ฉด, ํ•œ๊ธ€๊ณผ_์•ŒํŒŒ๋ฒณ_์ˆซ์ž๋ฅผ_์ œ์™ธํ•œ_๋ชจ๋“ _๋ฌธ์ž๋Š”_์•ˆ๋œ๋‹ค๊ฐ€ ๋‘ ๊ฐœ๋กœ ์ชผ๊ฐœ์ง„ ๋’ค ์ฒซ ๋ฒˆ์งธ๋Š” ์‹คํŒจ, ๋‘ ๋ฒˆ์งธ๋Š” ์„ฑ๊ณตํ•˜์˜€์Œ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์ด๋Š” @Test์™€ @ParameterizedTest๋ฅผ ํ•จ๊ป˜ ์ผ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•œ ํ˜„์ƒ์ž…๋‹ˆ๋‹ค. @Test์™€ @ParameterizedTest๋Š” ๊ฐ๊ฐ ํ•œ ๋ฒˆ ํ…Œ์ŠคํŠธํ•˜๋Š”์ง€, ํŒŒ๋ผ๋ฏธํ„ฐ๋“ค์„ ๋„ฃ์–ด๋ณด๋ฉฐ ํ…Œ์ŠคํŠธํ•˜๋Š”์ง€์ผ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์–ด๋…ธํ…Œ์ด์…˜๋“ค์ด๋ฏ€๋กœ, ์ด๋“ค์„ ๊ฐ™์ด ์“ฐ๋ฉด ์ €๋ ‡๊ฒŒ ๋ถ„๋ฆฌ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.


2. @Test, @ParameterizedTest, @ValueSource ์ฐจ์ด

์‚ฌ์šฉ๋œ ์–ด๋…ธํ…Œ์ด์…˜๋“ค์˜ ์‚ฌ์šฉ๋ฒ•์„ ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ธฐ ์œ„ํ•ด, ์ž‘์„ฑ๋œ ๋ฌธ์„œ๋ฅผ ์ง์ ‘ ๋ฒˆ์—ญํ•ด ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@Test

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = Status.STABLE, since = "5.0") @Testable public @interface Test extends java.lang.annotation.Annotation

@Test๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ฆฐ ๋ฉ”์„œ๋“œ๊ฐ€ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ์ž„์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. @Test ๋ฉ”์„œ๋“œ๋Š” private์ด๋‚˜ static์ด๋ฉด ์•ˆ ๋˜๋ฉฐ, ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด์„œ๋„ ์•ˆ ๋ฉ๋‹ˆ๋‹ค. @Test ๋ฉ”์„œ๋“œ๋Š” ParameterResolvers์— ์˜ํ•ด ํ•ด๊ฒฐ๋˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์„ ํƒ์ ์œผ๋กœ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. @Test๋Š” ๋˜ํ•œ @Test์˜ ์˜๋ฏธ๋ฅผ ์ƒ์†ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ์กฐํ•ฉ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๋ฉ”ํƒ€ ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ˆœ์„œ
๊ธฐ๋ณธ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋Š” ๊ฒฐ์ •์ ์ด์ง€๋งŒ ์ผ๋ถ€๋Ÿฌ ๋ช…๋ฐฑํ•˜์ง€ ์•Š์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ •๋ ฌ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ์˜ ํ›„์† ์‹คํ–‰์—์„œ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋ฅผ ๋™์ผํ•œ ์ˆœ์„œ๋กœ ์‹คํ–‰ํ•˜์—ฌ ๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•œ ๋นŒ๋“œ๋ฅผ ๊ฐ€๋Šฅ์ผ€ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋Š” ์ง์ ‘์ ์œผ๋กœ @Test, @RepeatedTest, @ParameterizedTest, @TestFactory ๋˜๋Š” @TestTemplate์œผ๋กœ ์–ด๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ฆฐ ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ์ธ ์–ด๋–ค ๊ฒƒ์ด๋“  ๋ฉ๋‹ˆ๋‹ค. ์ง„์ •ํ•œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์‹คํ–‰ ์ˆœ์„œ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋˜์ง€๋งŒ, ๋•Œ๋กœ๋Š” ๋ช…์‹œ์ ์œผ๋กœ ํŠน์ •ํ•œ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ๊ฐ•์ œํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. — ์˜ˆ๋ฅผ ๋“ค์–ด, ์‹œํ€€์Šค๊ฐ€ ์ค‘์š”ํ•œ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋˜๋Š” ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ, ํŠนํžˆ @TestInstance(Lifecycle.PER_CLASS)์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋  ๋•Œ์ž…๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ์ œ์–ดํ•˜๋ ค๋ฉด ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ๋˜๋Š” ํ…Œ์ŠคํŠธ ์ธํ„ฐํŽ˜์ด์Šค์— @TestMethodOrder๋ฅผ ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๋‹ฌ๊ณ  ์›ํ•˜๋Š” MethodOrderer ๊ตฌํ˜„์„ ์ง€์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

@ParameterizedTest

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = Status.STABLE, since = "5.7") @TestTemplate @ExtendWith({org.junit.jupiter.params.ParameterizedTestExtension.class}) public @interface ParameterizedTest extends java.lang.annotation.Annotation

@ParameterizedTest๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ฆฐ ๋ฉ”์„œ๋“œ๊ฐ€ ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”๋œ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ์ž„์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฉ”์„œ๋“œ๋Š” private์ด๋‚˜ static์ด์–ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค.

์ธ์ž ์ œ๊ณต ๋ฐ ์†Œ์Šค
@ParameterizedTest ๋ฉ”์„œ๋“œ๋Š” @ArgumentsSource ๋˜๋Š” ํ•ด๋‹นํ•˜๋Š” ๊ตฌ์„ฑ ์–ด๋…ธํ…Œ์ด์…˜(์˜ˆ: @ValueSource, @CsvSource ๋“ฑ)์„ ํ†ตํ•ด ์ ์–ด๋„ ํ•˜๋‚˜์˜ ArgumentsProvider๋ฅผ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ”„๋กœ๋ฐ”์ด๋”๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”๋œ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋  Argument์˜ ์ŠคํŠธ๋ฆผ์„ ์ œ๊ณตํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

ํ˜•์‹ ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ชฉ๋ก
@ParameterizedTest ๋ฉ”์„œ๋“œ๋Š” ๋ฉ”์„œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ชฉ๋ก ๋์— ๋‹ค๋ฅธ ParameterResolver๋“ค์— ์˜ํ•ด ํ•ด๊ฒฐ๋  ์ถ”๊ฐ€์ ์ธ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ตฌ์ฒด์ ์œผ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”๋œ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋Š” ๋‹ค์Œ ๊ทœ์น™์— ๋”ฐ๋ผ ํ˜•์‹ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ•˜๋‚˜ ์ด์ƒ์˜ ์ธ๋ฑ์‹ฑ๋œ ์ธ์ž๋Š” ๋จผ์ € ์„ ์–ธ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋‹ค์Œ์— ํ•˜๋‚˜ ์ด์ƒ์˜ ์–ด๊ทธ๋ฆฌ๊ฒŒ์ดํ„ฐ๊ฐ€ ์„ ์–ธ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ParameterResolver ๊ตฌํ˜„์—์„œ ์ œ๊ณต๋œ ํ•˜๋‚˜ ์ด์ƒ์˜ ์ธ์ž๋Š” ๋งˆ์ง€๋ง‰์— ์„ ์–ธ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ฌธ๋งฅ์—์„œ ์ธ๋ฑ์‹ฑ๋œ ์ธ์ž๋Š” ArgumentsProvider์— ์˜ํ•ด ์ œ๊ณต๋œ Arguments์˜ ์ฃผ์–ด์ง„ ์ธ๋ฑ์Šค์— ๋Œ€ํ•œ ์ธ์ž๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์ด๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”๋œ ๋ฉ”์„œ๋“œ์˜ ํ˜•์‹ ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ชฉ๋ก์—์„œ ๋™์ผํ•œ ์ธ๋ฑ์Šค์— ๋Œ€ํ•œ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ์–ด๊ทธ๋ฆฌ๊ฒŒ์ดํ„ฐ๋Š” ArgumentsAccessor ํ˜•์‹์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ๋˜๋Š” @AggregateWith๋กœ ์–ด๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ฆฐ ๋งค๊ฐœ๋ณ€์ˆ˜์ž…๋‹ˆ๋‹ค.

์ธ์ž ๋ณ€ํ™˜
๋ฉ”์„œ๋“œ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ๋ช…์‹œ์  ArgumentConverter๋ฅผ ์ง€์ •ํ•˜๊ธฐ ์œ„ํ•ด @ConvertWith ๋˜๋Š” ํ•ด๋‹น ๊ตฌ์„ฑ ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๋‹ฌ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด JUnit Jupiter๋Š” ๋Œ€์ƒ ํ˜•์‹์œผ๋กœ์˜ ์•”์‹œ์  ๋ณ€ํ™˜์„ ์ž๋™์œผ๋กœ ์‹œ๋„ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค(์ž์„ธํ•œ ๋‚ด์šฉ์€ ์‚ฌ์šฉ์ž ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”).

์กฐํ•ฉ๋œ ์–ด๋…ธํ…Œ์ด์…˜
@ParameterizedTest๋Š” @ParameterizedTest์˜ ์˜๋ฏธ๋ฅผ ์ƒ์†ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ์กฐํ•ฉ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๋ฉ”ํƒ€ ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ๋„ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ˆœ์„œ
๊ธฐ๋ณธ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋Š” ๊ฒฐ์ •์ ์ด์ง€๋งŒ ์ผ๋ถ€๋Ÿฌ ๋ช…๋ฐฑํ•˜์ง€ ์•Š์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ •๋ ฌ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ์˜ ํ›„์† ์‹คํ–‰์—์„œ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋ฅผ ๋™์ผํ•œ ์ˆœ์„œ๋กœ ์‹คํ–‰ํ•˜์—ฌ ๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•œ ๋นŒ๋“œ๋ฅผ ๊ฐ€๋Šฅ์ผ€ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋Š” ์ง์ ‘์ ์œผ๋กœ @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, ๋˜๋Š” @TestTemplate์œผ๋กœ ์–ด๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ฆฐ ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ์ธ ์–ด๋–ค ๊ฒƒ์ด๋“  ๋ฉ๋‹ˆ๋‹ค. ์ง„์ •ํ•œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ์ˆœ์„œ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ ๋˜์ง€๋งŒ, @TestMethodOrder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ @TestInstance(Lifecycle.PER_CLASS)์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋Š” ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋‚˜ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ํ…Œ์ŠคํŠธ์˜ ์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ์˜ ์‹คํ–‰ ์ˆœ์„œ๋ฅผ ์ œ์–ดํ•˜๋ ค๋ฉด ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ๋˜๋Š” ํ…Œ์ŠคํŠธ ์ธํ„ฐํŽ˜์ด์Šค์— @TestMethodOrder๋ฅผ ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๋‹ฌ๊ณ  ์›ํ•˜๋Š” MethodOrderer ๊ตฌํ˜„์„ ์ง€์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

@ValueSource

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @API(status = Status.STABLE, since = "5.7") @ArgumentsSource(org.junit.jupiter.params.provider.ValueArgumentsProvider.class) public @interface ValueSource extends java.lang.annotation.Annotation

@ValueSource๋Š” ๋ฆฌํ„ฐ๋Ÿด ๊ฐ’์˜ ๋ฐฐ์—ด์— ์•ก์„ธ์Šค ํ•˜๋Š” ArgumentsSource์ž…๋‹ˆ๋‹ค. ์ง€์›๋˜๋Š” ์œ ํ˜•์€ short, byte, int, long, float, double, char, boolean, String ๋ฐ Class๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ @ValueSource ์„ ์–ธ๋‹น ํ•˜๋‚˜์˜ ์ง€์›๋˜๋Š” ์œ ํ˜•๋งŒ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ๊ณต๋œ ๋ฆฌํ„ฐ๋Ÿด ๊ฐ’์€ ์–ด๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ฆฐ @ParameterizedTest ๋ฉ”์„œ๋“œ์— ์ธ์ˆ˜๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

 

๋‹ค์†Œ ์„ค๋ช…์ด ๊ธธ์ง€๋งŒ, ํ•ต์‹ฌ์„ ์ •๋ฆฌํ•ด ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • @Test ๋ฉ”์„œ๋“œ๋Š” ParameterResolver์— ์˜ํ•ด ํ•ด๊ฒฐ๋˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์„ ํƒ์ ์œผ๋กœ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • @ParameterizedTest๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” @ValueSource, @CsvSource ๋“ฑ์„ ํ†ตํ•ด ์ ์–ด๋„ ํ•˜๋‚˜์˜ ArgumentsProvider๋ฅผ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • @ValueSource๋Š” @Parameterized์— ์ธ์ž๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

3. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ๋‚ด๋ถ€์  ํ๋ฆ„

์ด์ œ ํ•ต์‹ฌ์œผ๋กœ ๋“ค์–ด๊ฐ€๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค! ๋‚ด์žฅ๋œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•ด ๋ณด๋ฉด, ParameterResolver์˜ ๊ตฌํ˜„์ฒด๋“ค์—์„œ supportsParameter๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์œ„์— ์žˆ๋˜ ParameterResolver์— ์˜ํ•ด ํ•ด๊ฒฐ๋˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์„ ํƒ์ ์œผ๋กœ ์„ ์–ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.์— ์˜ํ•ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

ParameterResolver์˜ ๊ตฌํ˜„์ฒด๋“ค์€ ์œ„์˜ 7๊ฐœ ์ž…๋‹ˆ๋‹ค.

 

์ด๊ฒƒ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์€ ExecutableInvoker ๋•๋ถ„์ž…๋‹ˆ๋‹ค.

ExecutableInvoker

ExecutableInvoker๊ฐ€ resolveParameters ๋ฉ”์„œ๋“œ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ๊ฐ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋งˆ๋‹ค ์ง€์›๋˜๋Š” ๋ฆฌ์กธ๋ฒ„๊ฐ€ ์žˆ๋Š”์ง€ resolveParameter๋ฅผ ํ˜ธ์ถœํ•จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

resolveParameter๋Š” ๋น„์›Œ์ ธ ์žˆ์„ ์‹œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ง€์›๋˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฆฌ์กธ๋ฒ„๊ฐ€ ๋‘ ๊ฐœ ์ด์ƒ์ผ ๊ฒฝ์šฐ์—๋„ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๋ฆฌ์กธ๋ฒ„๊ฐ€ ๋‘ ๊ฐœ ์ด์ƒ์ผ ์‹œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์‹œ ํ๋ฆ„์„ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

stream(ParameterResolver.class), if(matchingResolvers.isEmpty())์— ์ฃผ๋ชฉํ•ด์ฃผ์„ธ์š”!

ParameterContext, Executable executable, ExtensionContext, ExtensionRegistry ๋“ฑ์ด ๋ณด์ž…๋‹ˆ๋‹ค. ๋””๋ฒ„๊น…์„ ํ•ด ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ’๋“ค์ด ์ €์žฅ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.



์ฆ‰, extensionRegistry์—์„œ stream ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•œ ๋’ค matchingResolvers์˜ ์‚ฌ์ด์ฆˆ๊ฐ€ ๋น„์–ด์žˆ์œผ๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์ ‘ํ–ˆ๋˜ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค.

 

resolveParameter ํ•จ์ˆ˜์—์„œ ์ง€์ •๋œ extensionRegistry์˜ ๊ฐ’์œผ๋กœ MutableExtensionRegistry๊ฐ€ ๋“ค์–ด์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿฌ๋ฉด ์ด์ œ MutableExtensionRegistry๋ฅผ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

MutableExtensionRegistry

 

์ฝ”๋“œ๋ฅผ ๋œฏ์–ด๋ณด๋ฉด, ExtensionRegistry์˜ ๊ตฌํ˜„์ฒด๋กœ๋Š” MutableExtensionRegistry๋งŒ ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์•„๋ž˜๋Š” ExtensionRegistry์— ์ •์˜๋œ stream ๋ฉ”์„œ๋“œ์˜ ์šฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

 

 

Extension์„ ์ƒ์†๋ฐ›์€ ํด๋ž˜์Šค๋“ค ์ค‘, extensionType์— ํ•ด๋‹น๋˜๋Š” ๊ฒƒ๋“ค์„ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿฐ๋ฐ ์•„๊นŒ ์œ„์—์„œ ๋ณด๋ฉด, extensionType์ด ParameterResolver์˜€์Šต๋‹ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ ParameterResolver์˜ ๊ตฌํ˜„์ฒด (ParameterResolver ์ž์ฒด๊ฐ€ Extension์„ ์ƒ์†๋ฐ›์Šต๋‹ˆ๋‹ค.)๋“ค์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

 

์ด ๊ตฌํ˜„์ฒด๋“ค์—๊ฒŒ ์ „๋ถ€ supportsParameter(parameterContext, extensionContext)๋ฅผ ๋ฌผ์–ด๋ด…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  true๋กœ ์‘๋‹ตํ•˜๋Š” ๊ตฌํ˜„์ฒด๋“ค์„ ๋ฆฌ์ŠคํŠธ๋กœ ์ทจํ•ฉํ•œ ๋’ค  ๋น„์–ด ์žˆ๋‹ค๋ฉด ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.


4. ๊ฐ ParameterResolver ๊ตฌํ˜„์ฒด์˜ supportsParameter ํŒ์ • ๋ฐฉ์‹ ์•Œ์•„๋ณด๊ธฐ

๋Œ€ํ‘œ์ ์œผ๋กœ TempDirectory, TestInfoParameterResolver, TestReporterParameterResolver, ParameterizedTestParameterResolver์˜ ์›๋ฆฌ๋ฅผ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

TempDirectory

TempDirectory์˜ ๋‚ด๋ถ€ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

// Determine if the Parameter in the supplied ParameterContext is annotated with @TempDir.
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
    boolean annotated = parameterContext.isAnnotated(TempDir.class);
    if (annotated && parameterContext.getDeclaringExecutable() instanceof Constructor) {
        throw new ParameterResolutionException(
            "@TempDir is not supported on constructor parameters. Please use field injection instead.");
    }
    return annotated;
}

 

์šฐ๋ฆฌ๋Š” void ํ•œ๊ธ€๊ณผ_์•ŒํŒŒ๋ฒณ_์ˆซ์ž๋ฅผ_์ œ์™ธํ•œ_๋ชจ๋“ _๋ฌธ์ž๋Š”_์•ˆ๋œ๋‹ค(final String value)์™€ ๊ฐ™์ด ํŒŒ๋ผ๋ฏธํ„ฐ์— ๋‹จ์ˆœ String์„ ๋„ฃ์—ˆ์—ˆ์Šต๋‹ˆ๋‹ค. @TempDir์ด ๋‹ฌ๋ ค์žˆ์ง€ ์•Š๊ธฐ์—, ์œ„์˜ supportsParameter์—์„œ๋Š” false๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

์š”์•ฝํ•˜์ž๋ฉด parameterContext๋Š” ํ…Œ์ŠคํŠธ ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ (์ธ์ž)๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, extensionContext๋Š” ํ…Œ์ŠคํŠธ ํ•จ์ˆ˜ ๋‚ด์šฉ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

 

์—ฌ๋‹ด์œผ๋กœ, TempDir์„ ์•„๋ž˜์ฒ˜๋Ÿผ ์ธ์ž์— ๋„ฃ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ  getDeclaringExecutable์„ ๋ณด๋ฉด ๋ฉ”์„œ๋“œ์ธ์ง€ ์ƒ์„ฑ์ž์ธ์ง€๋ฅผ ๊ตฌ๋ณ„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋จ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

java.lang.reflect.Method or java.lang.reflect.Constructor๋ผ๊ณ  ๋‚˜์™€ ์žˆ์Šต๋‹ˆ๋‹ค!

TestInfoParameterResolver

TestInfoParameterResolver๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

parameterContext.getParameter๋Š” Parameter๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉฐ, ์ด๊ฒƒ์˜ getType ๋ฉ”์„œ๋“œ๋ฅผ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

tmp์— String์ด ๋“ค์–ด๊ฐ„ ๊ฒƒ์ด ๋ณด์ด์‹œ๋‚˜์š”? ์ด๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ธ์ž์— String์„ ๋„ฃ์–ด๋†จ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

 

์ฆ‰, String์€ TestInfo.class๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋˜ํ•œ false๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

 

TestReporterParameterResolver

TestReporterParameterResolver๊ฐ€ ์•ˆ ๋˜๋Š” ์ด์œ ๋„ ๋น„์Šทํ•ฉ๋‹ˆ๋‹ค. TestReporter.class๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— false๋กœ ๋ฉ๋‹ˆ๋‹ค.

 

ParameterizedTestParameterResolver, RepetitionInfoParameterResolver, SoftAssertionsExtension, TypeBasedParameterResolver์€ ๋””๋ฒ„๊ทธ ๋ธŒ๋ ˆ์ดํฌ ํฌ์ธํŠธ๋ฅผ ๊ฑธ์–ด๋„ ํ˜ธ์ถœ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์•„๋งˆ @Test์ผ ๋•Œ๋Š” ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์€๋ฐ, ์ด ๋ถ€๋ถ„์€ ๋” ์•Œ์•„๋ด์•ผ ํ•  ๋“ฏํ•ฉ๋‹ˆ๋‹ค.

 

์ง€๊ธˆ๊นŒ์ง€ @Test๋ฅผ ๋ถ™์ธ ๋’ค ์ธ์ž์— String ๋“ฑ ์ผ๋ฐ˜์ ์ธ ํƒ€์ž…์„ ์ž‘์„ฑํ•ด ๋‘๋ฉด matchingResolvers๊ฐ€ ๋น„์›Œ์ง„ ์ƒํƒœ๋ผ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ž„์„ ์•Œ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๋งˆ์ง€๋ง‰์œผ๋กœ ParameterizedTestParameterResolver๋Š” ์–ด๋–ค์ง€ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

ParameterizedTestParameterResolver

ParameterizedTestParameterResolver์˜ ๊ฒฝ์šฐ์—๋Š” @Test์ผ ์‹œ์—๋Š” ํ˜ธ์ถœ๋˜์ง€ ์•Š๊ณ , @ParameterizedTest์ผ ์‹œ์—๋งŒ ๋””๋ฒ„๊น…์— ๊ฑธ๋ ธ์Œ์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”!

 

declaringExecutable, testMethod, parameterIndex๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

 

๋ณด๋Š” ๋ฐ”์™€ ๊ฐ™์ด, declaringExecutable๊ณผ testMethod๊ฐ€ ๊ฐ™์œผ๋ฏ€๋กœ ํ†ต๊ณผ๋˜๋ฉฐ, ๊ทธ ์ดํ›„์˜ ์กฐ๊ฑด๋“ค๋„ ๋ฌธ์ œ์—†์ด ํ†ต๊ณผ๋ฉ๋‹ˆ๋‹ค. (Aggrregator์— ๋Œ€ํ•ด์„œ๋Š” @ParameterizedTest์˜ ์„ค๋ช…์— ๊ธฐ๋ก๋˜์–ด ์žˆ๊ธด ํ•˜์ง€๋งŒ, ์ •ํ™•ํžˆ ๋ฌด์—‡์ธ์ง€ ๋” ์•Œ์•„๋ด์•ผ ํ•  ๋“ฏํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ จ ํด๋ž˜์Šค๋Š” ParameterizedTestMethodContext์ž…๋‹ˆ๋‹ค.)

 

@ParameterizedTest๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์‚ฌ์šฉํ•˜์˜€์„ ๊ฒฝ์šฐ, ๋งˆ์ง€๋ง‰์˜ return ๋ฌธ์— ๋„๋‹ฌํ•˜์—ฌ true๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.


๊ฒฐ๋ก 

๊ฐœ์ธ์ ์ธ ์ถ”์ธก์ด์—ˆ์ง€๋งŒ, ์ง€๊ธˆ๊นŒ์ง€์˜ ์‹œ๋„๋กœ ๋ฏธ๋ฃจ์–ด๋ดค์„ ๋•Œ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์‹คํ–‰ ์‹œ ์ž‘์„ฑ๋œ ๊ฐ๊ฐ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์ข…๋ฅ˜์— ๋”ฐ๋ผ ์ง€์› ๊ฐ€๋Šฅํ•œ ParameterResolver๋“ค์ด ๋‚˜์˜ค๊ณ , ์•„์˜ˆ ๊ฐ€๋Šฅํ•œ ๊ฒŒ ์—†๋‹ค๋ฉด ์˜ˆ์™ธ๊ฐ€ ํ„ฐ์ง„๋‹ค๋Š” ์ ์„ ์•Œ์•„๋ƒˆ์Šต๋‹ˆ๋‹ค.

 

supportsParameter์˜ ์ธ์ž์—์„œ ParameterContext (ํŒŒ๋ผ๋ฏธํ„ฐ ์ •๋ณด)์™€ ExtensionContext (ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ค‘ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ™•์žฅ ์ฝ˜ํ…์ŠคํŠธ ์ •๋ณด)๊ฐ€ ์“ฐ์ธ๋‹ค๋Š” ์ ์—์„œ, ๊ฐ์ข… ์กฐํ•ฉ์— ๋”ฐ๋ฅธ ๋‹ต์ด ์žˆ๋Š” ๋“ฏํ•ฉ๋‹ˆ๋‹ค.

 

๊ทธ๋ฆฌ๊ณ , @Test์™€ @ParameterizedTest์ž„์— ๋”ฐ๋ผ์„œ String ์ธ์ž๋ฅผ ๋ถ™์—ฌ๋„ ๋˜๊ณ , ๋ถ™์ด์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒŒ ๋‚˜๋‰˜๊ธฐ ๋•Œ๋ฌธ์— ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ @Test์™€ @ParameterizedTest๋ฅผ ํ˜ผ์šฉํ•˜๋ฉด ์•ˆ ๋œ๋‹ค๋Š” ์ ๋„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. (@Test์ผ ๋•Œ๋Š” String ์ธ์ž๋ฅผ ๋ถ™์ด๋ฉด ์•ˆ ๋˜์ง€๋งŒ, @ParameterizedTest์—์„œ๋Š” ๋ถ™์—ฌ๋„ ๋˜์ฃ !)

 

์ŠคํŠธ๋ฆผ๊ณผ ์ œ๋„ค๋ฆญ, Aggregator ๋“ฑ ์•„์ง๋„ ๋ฐฐ์šธ ๊ฒŒ ๋งŽ๋‹ค๋Š” ๊ฒƒ์„ ์ƒˆ์‚ผ ๋Š๊ผˆ๊ณ , ์ž‘๊ฒŒ๋‚˜๋งˆ JUnit5์˜ ์ด๋Ÿฌํ•œ ๋‚ด๋ถ€ ์›๋ฆฌ๋ฅผ ํŒŒ์•…ํ•˜๊ฒŒ ๋œ ๊ฒƒ ๊ฐ™์•„ ์œ ์ตํ•œ ๊ฒฝํ—˜์ด์—ˆ์Šต๋‹ˆ๋‹ค.

 

์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ๋กํ•˜๋‹ค ๋ณด๋‹ˆ ํ‹€๋ฆฐ ์ •๋ณด๋“ค์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณ ์น  ์ ์ด ์žˆ๋‹ค๋ฉด ๋ฐ”๋กœ ์ˆ˜์ •ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!