์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ ์Šคํ”„๋ง ๋ถ€ํŠธ ํ…Œ์ŠคํŠธ: @SpringBootTest ํ™œ์šฉํ•˜๊ธฐ ๐Ÿงช

๐Ÿš€ ์Šคํ”„๋ง ๋ถ€ํŠธ ํ…Œ์ŠคํŠธ: @SpringBootTest ํ™œ์šฉํ•˜๊ธฐ ๐Ÿงช

์•ˆ๋…•, ์นœ๊ตฌ๋“ค! ์˜ค๋Š˜์€ ์ •๋ง ํฅ๋ฏธ์ง„์ง„ํ•œ ์ฃผ์ œ๋กœ ์ฐพ์•„์™”์–ด. ๋ฐ”๋กœ ์Šคํ”„๋ง ๋ถ€ํŠธ ํ…Œ์ŠคํŠธ์™€ @SpringBootTest ์–ด๋…ธํ…Œ์ด์…˜์— ๋Œ€ํ•ด ๊นŠ์ด ํŒŒํ—ค์ณ๋ณผ ๊ฑฐ์•ผ. ๐Ÿ˜Ž ์ด ๊ธ€์„ ์ฝ๊ณ  ๋‚˜๋ฉด ๋„ˆํฌ๋„ ํ…Œ์ŠคํŠธ ๋งˆ์Šคํ„ฐ๊ฐ€ ๋  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ์ž, ์ค€๋น„๋๋‹ˆ? ๊ทธ๋Ÿผ ์‹œ์ž‘ํ•ด๋ณด์ž๊ณ !

์ž ๊น! ํ˜น์‹œ ๋„ˆํฌ ์ค‘์— ์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์ด๋ผ๋Š” ์‚ฌ์ดํŠธ ๋“ค์–ด๋ดค์–ด? ์—ฌ๊ธฐ์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์‹ค๋ ฅ์„ ๊ณต์œ ํ•˜๊ณ  ๋ฐฐ์šธ ์ˆ˜ ์žˆ๋Œ€. ๋‚˜์ค‘์— ์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์šด ์Šคํ”„๋ง ๋ถ€ํŠธ ํ…Œ์ŠคํŠธ ๊ธฐ์ˆ ๋กœ ๋ฉ‹์ง„ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ ๊ณต์œ ํ•ด๋ณด๋Š” ๊ฑด ์–ด๋•Œ? ๐Ÿค”

1. ์Šคํ”„๋ง ๋ถ€ํŠธ ํ…Œ์ŠคํŠธ๋ž€ ๋ญ์•ผ? ๐Ÿคทโ€โ™‚๏ธ

์ž, ๋จผ์ € ์Šคํ”„๋ง ๋ถ€ํŠธ ํ…Œ์ŠคํŠธ๊ฐ€ ๋ญ”์ง€๋ถ€ํ„ฐ ์•Œ์•„๋ณด์ž. ๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ, ์Šคํ”„๋ง ๋ถ€ํŠธ ํ…Œ์ŠคํŠธ๋Š” ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์•ผ. ๋งˆ์น˜ ์š”๋ฆฌ์‚ฌ๊ฐ€ ์Œ์‹ ๋ง›์„ ๋ณด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ์šฐ๋ฆฌ๋„ ์ฝ”๋“œ์˜ '๋ง›'์„ ๋ณด๋Š” ๊ฑฐ์ง€! ๐Ÿ˜‹

์Šคํ”„๋ง ๋ถ€ํŠธ ํ…Œ์ŠคํŠธ๋Š” ์šฐ๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ’ˆ์งˆ์„ ๋ณด์žฅํ•˜๊ณ , ๋ฒ„๊ทธ๋ฅผ ๋ฏธ๋ฆฌ ์žก์•„๋‚ด๋Š” ๋ฐ ํฐ ๋„์›€์„ ์ค˜.

์ด๊ฒŒ ์™œ ์ค‘์š”ํ•˜๋ƒ๊ณ ? ์ƒ์ƒํ•ด๋ด. ๋„ค๊ฐ€ ๋งŒ๋“  ์•ฑ์ด ์ˆ˜๋งŽ์€ ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ ๋ฐฐํฌ๋๋Š”๋ฐ, ๊ฐ‘์ž๊ธฐ ํฐ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด? ๐Ÿ˜ฑ ์•„์ฐ”ํ•˜์ง€ ์•Š์•„? ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์ด๋Ÿฐ ์ผ์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋„๋ก ๋ฏธ๋ฆฌ๋ฏธ๋ฆฌ ์ค€๋น„ํ•˜๋Š” ๊ฑฐ์•ผ. ์Šคํ”„๋ง ๋ถ€ํŠธ ํ…Œ์ŠคํŠธ ๊ฐœ๋…๋„ ์Šคํ”„๋ง ๋ถ€ํŠธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๊ฒฐ๊ณผ ํ™•์ธ

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

2. @SpringBootTest ์–ด๋…ธํ…Œ์ด์…˜์˜ ๋น„๋ฐ€ ๐Ÿ•ต๏ธโ€โ™€๏ธ

์ž, ์ด์ œ ์šฐ๋ฆฌ์˜ ์ฃผ์ธ๊ณต @SpringBootTest ์–ด๋…ธํ…Œ์ด์…˜์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ์ฐจ๋ก€์•ผ. ์ด ๋…€์„, ๊ฒ‰๋ณด๊ธฐ์—” ๋ณ„ ๊ฑฐ ์•„๋‹Œ ๊ฒƒ ๊ฐ™์ง€๋งŒ, ์‹ค์€ ์—„์ฒญ๋‚œ ํž˜์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด!

@SpringBootTest๋Š” ์Šคํ”„๋ง ๋ถ€ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ์†์‰ฝ๊ฒŒ ๊ตฌ์„ฑํ•ด์ฃผ๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ์–ด๋…ธํ…Œ์ด์…˜์ด์•ผ.

์ด ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด, ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๊ฑฐ์˜ ๋™์ผํ•œ ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์ง€. coolํ•˜์ง€ ์•Š์•„? ๐Ÿ˜Ž

๐Ÿšจ ์ฃผ์˜! @SpringBootTest๋Š” ๊ฐ•๋ ฅํ•˜์ง€๋งŒ, ๊ทธ๋งŒํผ ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•ด. ๋ชจ๋“  ํ…Œ์ŠคํŠธ์— ๋ฌด์ž‘์ • ์‚ฌ์šฉํ•˜๋ฉด ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ๊ฐ„์ด ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์œผ๋‹ˆ ๊ผญ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•˜์ž!

์ž, ์ด์ œ @SpringBootTest๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณด์ž.


import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MyAwesomeTest {

    @Test
    void contextLoads() {
        // ์—ฌ๊ธฐ์„œ ํ…Œ์ŠคํŠธ ๋กœ์ง์„ ์ž‘์„ฑํ•ด
    }
}

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

ํ•˜์ง€๋งŒ ์ด๊ฒŒ ๋‹ค๊ฐ€ ์•„๋‹ˆ์•ผ! @SpringBootTest๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์˜ต์…˜์„ ์ œ๊ณตํ•ด์„œ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด:

  • webEnvironment: ์›น ํ™˜๊ฒฝ ์„ค์ •
  • properties: ํ…Œ์ŠคํŠธ์šฉ ํ”„๋กœํผํ‹ฐ ์„ค์ •
  • classes: ํŠน์ • ์„ค์ • ํด๋ž˜์Šค๋งŒ ๋กœ๋“œ

์ด๋Ÿฐ ์˜ต์…˜๋“ค์„ ํ™œ์šฉํ•˜๋ฉด ์ •๋ง ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์— ๋งž๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด. ์˜ˆ๋ฅผ ๋“ค์–ด, ์›น ์„œ๋ฒ„๋ฅผ ์‹ค์ œ๋กœ ๋„์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด ์ด๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์žˆ์ง€:


@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyWebTest {
    // ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์‹ค์ œ ์›น ์„œ๋ฒ„๊ฐ€ ๋žœ๋ค ํฌํŠธ๋กœ ์‹œ์ž‘๋˜๊ณ , ๊ทธ ์ƒํƒœ์—์„œ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํ–‰๋ผ. ๋งˆ์น˜ ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ๊ณผ ๋น„์Šทํ•œ ์ƒํ™ฉ์—์„œ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฑฐ์ง€!

3. @SpringBootTest์˜ ๋‚ด๋ถ€ ๋™์ž‘ ์›๋ฆฌ ๐Ÿ”

์ž, ์ด์ œ @SpringBootTest๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ๋” ์ž์„ธํžˆ ๋“ค์—ฌ๋‹ค๋ณผ ์‹œ๊ฐ„์ด์•ผ. ์ด ๋…€์„์ด ์–ด๋–ป๊ฒŒ ๊ทธ๋ ‡๊ฒŒ ๊ฐ•๋ ฅํ•œ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๊ฑธ๊นŒ? ๐Ÿค”

@SpringBootTest๋Š” ์Šคํ”„๋ง ๋ถ€ํŠธ์˜ ์ž๋™ ๊ตฌ์„ฑ(Auto-configuration) ๊ธฐ๋Šฅ์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•ด.

์ด๊ฒŒ ๋ฌด์Šจ ๋ง์ด๋ƒ๋ฉด, ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹œ์ž‘ํ•  ๋•Œ์ฒ˜๋Ÿผ ํ•„์š”ํ•œ ๋ชจ๋“  ๋นˆ(Bean)๋“ค์„ ์ž๋™์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ๊ตฌ์„ฑํ•œ๋‹ค๋Š” ๊ฑฐ์•ผ. ์ •๋ง ๋˜‘๋˜‘ํ•˜์ง€?

@SpringBootTest์˜ ๋™์ž‘ ๊ณผ์ •์„ ๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•˜๋ฉด ์ด๋ ‡๊ฒŒ ๋ผ:

  1. ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค๋ฅผ ์Šค์บ”ํ•˜๊ณ  @SpringBootTest ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ฐœ๊ฒฌํ•ด.
  2. ์Šคํ”„๋ง ๋ถ€ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•ด.
  3. @SpringBootApplication์ด ๋ถ™์€ ๋ฉ”์ธ ํด๋ž˜์Šค๋ฅผ ์ฐพ์•„ ๊ตฌ์„ฑ ์ •๋ณด๋กœ ์‚ฌ์šฉํ•ด.
  4. application.properties (๋˜๋Š” application.yml) ํŒŒ์ผ์˜ ์„ค์ •์„ ๋กœ๋“œํ•ด.
  5. ์ž๋™ ๊ตฌ์„ฑ์„ ํ†ตํ•ด ํ•„์š”ํ•œ ๋นˆ๋“ค์„ ๋“ฑ๋กํ•˜๊ณ  ์ดˆ๊ธฐํ™”ํ•ด.
  6. ํ…Œ์ŠคํŠธ์— ํ•„์š”ํ•œ ์ถ”๊ฐ€์ ์ธ ๊ตฌ์„ฑ์„ ์ ์šฉํ•ด (์˜ˆ: ์›น ํ™˜๊ฒฝ ์„ค์ •).
  7. ๋ชจ๋“  ์ค€๋น„๊ฐ€ ๋๋‚˜๋ฉด ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•ด.
@SpringBootTest ๋™์ž‘ ๊ณผ์ • @SpringBootTest ๋™์ž‘ ๊ณผ์ • ์–ด๋…ธํ…Œ์ด์…˜ ์Šค์บ” ์ปจํ…์ŠคํŠธ ์ƒ์„ฑ ์„ค์ • ๋กœ๋“œ ๋นˆ ๋“ฑ๋ก/์ดˆ๊ธฐํ™” ํ…Œ์ŠคํŠธ ์‹คํ–‰

์ด ๊ณผ์ •์„ ๊ฑฐ์น˜๋ฉด์„œ @SpringBootTest๋Š” ์šฐ๋ฆฌ๊ฐ€ ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ–ˆ์„ ๋•Œ์™€ ๊ฑฐ์˜ ๋™์ผํ•œ ํ™˜๊ฒฝ์„ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๊ฑฐ์•ผ. ๊ทธ๋ž˜์„œ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ์— ์™„๋ฒฝํ•œ ์กฐ๊ฑด์ด ๋˜๋Š” ๊ฑฐ์ง€!

ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์—๋Š” ๋‹น์—ฐํžˆ ๋Œ€๊ฐ€๊ฐ€ ๋”ฐ๋ฅด์ง€. ๋ฐ”๋กœ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ๊ฐ„์ด ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฑฐ์•ผ. ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ๋ฅผ ๋กœ๋“œํ•˜๋Š” ๊ฒŒ ๊ฝค ๋ฌด๊ฑฐ์šด ์ž‘์—…์ด๊ฑฐ๋“ . ๊ทธ๋ž˜์„œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์ฒ˜๋Ÿผ ๊ฐ€๋ฒผ์šด ํ…Œ์ŠคํŠธ์—๋Š” @SpringBootTest ๋Œ€์‹  ๋‹ค๋ฅธ ๊ฐ€๋ฒผ์šด ํ…Œ์ŠคํŠธ ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„.

๐Ÿ’ก ๊ฟ€ํŒ: ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ค„์ด๊ณ  ์‹ถ๋‹ค๋ฉด, ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค๋“ค ์‚ฌ์ด์— @DirtiesContext ์–ด๋…ธํ…Œ์ด์…˜์„ ์ ์ ˆํžˆ ์‚ฌ์šฉํ•ด๋ด. ์ด ์–ด๋…ธํ…Œ์ด์…˜์€ ์Šคํ”„๋ง์—๊ฒŒ "์ด ํ…Œ์ŠคํŠธ๊ฐ€ ๋๋‚˜๋ฉด ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ด"๋ผ๊ณ  ์•Œ๋ ค์ฃผ๋Š” ์—ญํ• ์„ ํ•ด. ์ปจํ…์ŠคํŠธ๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์—์„œ๋Š” ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์†๋„๋ฅผ ํฌ๊ฒŒ ๋†’์ผ ์ˆ˜ ์žˆ์ง€!

4. @SpringBootTest ์‹ค์ „ ํ™œ์šฉ ์˜ˆ์ œ ๐Ÿ’ช

์ž, ์ด์ œ @SpringBootTest๋ฅผ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€ ๋ช‡ ๊ฐ€์ง€ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณด์ž. ์ด๋ก ๋งŒ ์•Œ๋ฉด ๋ญํ•ด, ์‹ค์ „์—์„œ ์จ๋จน์„ ์ค„ ์•Œ์•„์•ผ์ง€! ๐Ÿ˜‰

4.1 ๊ธฐ๋ณธ์ ์ธ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ

๋จผ์ € ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํ˜•ํƒœ์˜ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณผ๊นŒ?


import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class UserServiceIntegrationTest {

    @Autowired
    private UserService userService;

    @Test
    void testCreateUser() {
        User user = new User("testuser", "test@example.com");
        User createdUser = userService.createUser(user);
        
        assertNotNull(createdUser.getId());
        assertEquals("testuser", createdUser.getUsername());
        assertEquals("test@example.com", createdUser.getEmail());
    }
}

์ด ํ…Œ์ŠคํŠธ์—์„œ๋Š” UserService๋ฅผ ์ฃผ์ž…๋ฐ›์•„ ์‹ค์ œ๋กœ ์‚ฌ์šฉ์ž๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์žˆ์–ด. @SpringBootTest ๋•๋ถ„์— ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋™์ผํ•œ ํ™˜๊ฒฝ์—์„œ UserService๊ฐ€ ์ œ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์ง€.

4.2 ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ…Œ์ŠคํŠธ

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ…Œ์ŠคํŠธํ•  ๋•Œ๋Š” ์‹ค์ œ HTTP ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์‘๋‹ต์„ ํ™•์ธํ•ด์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ์–ด. ์ด๋Ÿด ๋•Œ @SpringBootTest์™€ TestRestTemplate์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ์•„์ฃผ ํŽธ๋ฆฌํ•ด!


import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {

    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void testGetUser() {
        String url = "http://localhost:" + port + "/api/users/1";
        ResponseEntity<User> response = restTemplate.getForEntity(url, User.class);
        
        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertNotNull(response.getBody());
        assertEquals("testuser", response.getBody().getUsername());
    }
}

์—ฌ๊ธฐ์„œ๋Š” @SpringBootTest์˜ webEnvironment ์˜ต์…˜์„ RANDOM_PORT๋กœ ์„ค์ •ํ•ด์„œ ์‹ค์ œ ์›น ์„œ๋ฒ„๋ฅผ ๋žœ๋ค ํฌํŠธ๋กœ ์‹œ์ž‘ํ–ˆ์–ด. ๊ทธ๋ฆฌ๊ณ  TestRestTemplate์„ ์‚ฌ์šฉํ•ด HTTP GET ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ์‘๋‹ต์„ ํ™•์ธํ•˜๊ณ  ์žˆ์ง€. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ปจํŠธ๋กค๋Ÿฌ๋ถ€ํ„ฐ ์„œ๋น„์Šค, ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๊นŒ์ง€ ์ „์ฒด ํ”Œ๋กœ์šฐ๋ฅผ ํ•œ ๋ฒˆ์— ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์–ด!

4.3 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๋™ ํ…Œ์ŠคํŠธ

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์—ฐ๋™๋œ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•  ๋•Œ๋„ @SpringBootTest๊ฐ€ ํฐ ๋„์›€์ด ๋ผ. ์˜ˆ๋ฅผ ๋“ค์–ด, JPA๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฆฌํฌ์ง€ํ† ๋ฆฌ๋ฅผ ํ…Œ์ŠคํŠธํ•œ๋‹ค๊ณ  ํ•ด๋ณด์ž.


import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

@SpringBootTest
@Transactional
public class UserRepositoryIntegrationTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void testSaveAndFindUser() {
        User user = new User("testuser", "test@example.com");
        userRepository.save(user);
        
        User foundUser = userRepository.findByUsername("testuser");
        assertNotNull(foundUser);
        assertEquals("test@example.com", foundUser.getEmail());
    }
}

์ด ํ…Œ์ŠคํŠธ์—์„œ๋Š” @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์ถ”๊ฐ€๋กœ ์‚ฌ์šฉํ–ˆ์–ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๊ฐ€ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์‹คํ–‰๋˜๊ณ , ํ…Œ์ŠคํŠธ๊ฐ€ ๋๋‚˜๋ฉด ์ž๋™์œผ๋กœ ๋กค๋ฐฑ๋ผ. ๋•๋ถ„์— ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ ์‹ค์ œ DB์— ๋‚จ์ง€ ์•Š์•„์„œ ํ…Œ์ŠคํŠธ ๊ฒฉ๋ฆฌ์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์ง€.

4.4 ์™ธ๋ถ€ ์„œ๋น„์Šค ๋ชจํ‚น ํ…Œ์ŠคํŠธ

๋•Œ๋กœ๋Š” ์™ธ๋ถ€ ์„œ๋น„์Šค์— ์˜์กดํ•˜๋Š” ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ์–ด. ์ด๋Ÿด ๋•Œ๋Š” @MockBean์„ ์‚ฌ์šฉํ•ด์„œ ์™ธ๋ถ€ ์„œ๋น„์Šค๋ฅผ ๋ชจํ‚นํ•  ์ˆ˜ ์žˆ์–ด.


import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;

@SpringBootTest
public class WeatherServiceIntegrationTest {

    @Autowired
    private WeatherService weatherService;

    @MockBean
    private ExternalWeatherApi externalWeatherApi;

    @Test
    void testGetWeatherForecast() {
        when(externalWeatherApi.getForecast("Seoul"))
            .thenReturn(new Forecast("Sunny", 25));
        
        Weather weather = weatherService.getWeatherForecast("Seoul");
        
        assertEquals("Sunny", weather.getCondition());
        assertEquals(25, weather.getTemperature());
    }
}

์—ฌ๊ธฐ์„œ๋Š” ExternalWeatherApi๋ฅผ @MockBean์œผ๋กœ ์„ ์–ธํ•ด์„œ ์‹ค์ œ ์™ธ๋ถ€ API๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ ๋„ WeatherService๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ์–ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ๋‚˜ ์™ธ๋ถ€ ์„œ๋น„์Šค์˜ ๋ถˆ์•ˆ์ •์„ฑ์— ์˜ํ–ฅ๋ฐ›์ง€ ์•Š๊ณ  ์•ˆ์ •์ ์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์ง€.

๐Ÿšจ ์ฃผ์˜! @MockBean์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ด. ์ด ์–ด๋…ธํ…Œ์ด์…˜์€ ์Šคํ”„๋ง ์ปจํ…์ŠคํŠธ๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๊ณผ๋„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ๊ฐ„์ด ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์–ด. ๊ผญ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„!

5. @SpringBootTest์˜ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ ๐Ÿš€

์ž, ์ด์ œ @SpringBootTest์˜ ๋” ๊ณ ๊ธ‰์Šค๋Ÿฌ์šด ๊ธฐ๋Šฅ๋“ค์„ ์‚ดํŽด๋ณผ ์ฐจ๋ก€์•ผ. ์ด ๋…€์„์€ ์ •๋ง ๋‹ค์žฌ๋‹ค๋Šฅํ•ด์„œ, ์šฐ๋ฆฌ๊ฐ€ ์ƒ์ƒํ•˜๋Š” ๊ฑฐ์˜ ๋ชจ๋“  ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ปค๋ฒ„ํ•  ์ˆ˜ ์žˆ์–ด!

5.1 ํ…Œ์ŠคํŠธ์šฉ ์„ค์ • ์˜ค๋ฒ„๋ผ์ด๋”ฉ

๋•Œ๋กœ๋Š” ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ๋งŒ ํŠน๋ณ„ํ•œ ์„ค์ •์„ ์ ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์ง€? ์ด๋Ÿด ๋•Œ @SpringBootTest์˜ properties ์†์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ผ.


@SpringBootTest(properties = {
    "spring.datasource.url=jdbc:h2:mem:testdb",
    "spring.jpa.hibernate.ddl-auto=create-drop"
})
public class CustomConfigTest {
    // ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ ์ง€์ •ํ•œ ํ”„๋กœํผํ‹ฐ๋“ค์ด ๊ธฐ์กด ์„ค์ •์„ ์˜ค๋ฒ„๋ผ์ด๋“œํ•ด. ์˜ˆ๋ฅผ ๋“ค์–ด, ์œ„ ์ฝ”๋“œ์—์„œ๋Š” ํ…Œ์ŠคํŠธ์šฉ ์ธ๋ฉ”๋ชจ๋ฆฌ H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ–ˆ์–ด. ์‹ค์ œ ์šด์˜ DB๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฑฐ์ง€!

5.2 ํ…Œ์ŠคํŠธ ํ”„๋กœํŒŒ์ผ ํ™œ์„ฑํ™”

์Šคํ”„๋ง์˜ ํ”„๋กœํŒŒ์ผ ๊ธฐ๋Šฅ์„ ์ด์šฉํ•ด ํ…Œ์ŠคํŠธ์šฉ ์„ค์ •์„ ๋”ฐ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด @ActiveProfiles ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด.


@SpringBootTest
@ActiveProfiles("test")
public class TestProfileActivationTest {
    // ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด application-test.properties (๋˜๋Š” application-test.yml) ํŒŒ์ผ์˜ ์„ค์ •์ด ์ ์šฉ๋ผ. ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์— ํŠนํ™”๋œ ์„ค์ •์„ ๋ณ„๋„๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ์•„์ฃผ ํŽธ๋ฆฌํ•˜์ง€!

5.3 ํ…Œ์ŠคํŠธ์šฉ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ • ํด๋ž˜์Šค ์‚ฌ์šฉ

๋•Œ๋กœ๋Š” ํ…Œ์ŠคํŠธ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ํŠน๋ณ„ํ•œ ๋นˆ์ด๋‚˜ ์„ค์ •์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์–ด. ์ด๋Ÿด ๋•Œ๋Š” @TestConfiguration์„ ์‚ฌ์šฉํ•ด ํ…Œ์ŠคํŠธ์šฉ ์„ค์ • ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด.


@SpringBootTest
public class CustomTestConfigurationTest {

    @TestConfiguration
    static class TestConfig {
        @Bean
        public DataSource dataSource() {
            return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .build();
        }
    }

    @Autowired
    private DataSource dataSource;

    @Test
    void testCustomDataSource() {
        assertNotNull(dataSource);
        assertTrue(dataSource instanceof EmbeddedDatabase);
    }
}

์ด ์˜ˆ์ œ์—์„œ๋Š” ํ…Œ์ŠคํŠธ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ž„๋ฒ ๋””๋“œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์„ค์ •ํ•˜๊ณ  ์žˆ์–ด. @TestConfiguration ๋•๋ถ„์— ์ด ์„ค์ •์€ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ์—๋งŒ ์ ์šฉ๋˜๊ณ , ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰์—๋Š” ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์•„.

5.4 ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ์™€์˜ ์กฐํ•ฉ

@SpringBootTest๋Š” ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ๋ฅผ ๋กœ๋“œํ•˜์ง€๋งŒ, ๋•Œ๋กœ๋Š” ํŠน์ • ๋ ˆ์ด์–ด๋งŒ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ถ์„ ๋•Œ๊ฐ€ ์žˆ์–ด. ์ด๋Ÿด ๋•Œ ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ ์–ด๋…ธํ…Œ์ด์…˜๊ณผ ์กฐํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด.


@SpringBootTest
@AutoConfigureMockMvc
public class ControllerSliceTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void testGetUser() throws Exception {
        mockMvc.perform(get("/api/users/1"))
               .andExpect(status().isOk())
               .andExpect(jsonPath("$.username").value("testuser"));
    }
}

์ด ์˜ˆ์ œ์—์„œ๋Š” @SpringBootTest์™€ @AutoConfigureMockMvc๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด ์ปจํŠธ๋กค๋Ÿฌ ๋ ˆ์ด์–ด๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์žˆ์–ด. MockMvc๋ฅผ ์ด์šฉํ•ด HTTP ์š”์ฒญ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๊ณ , ์‘๋‹ต์„ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์ง€.

5.5 ๋น„๋™๊ธฐ ํ…Œ์ŠคํŠธ

๋งŒ์•ฝ ๋น„๋™๊ธฐ ์ž‘์—…์„ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•œ๋‹ค๋ฉด, @SpringBootTest์™€ ํ•จ๊ป˜ @Async ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด.


@SpringBootTest
public class AsyncServiceTest {

    @Autowired
    private AsyncService asyncService;

    @Test
    void testAsyncMethod() throws Exception {
        CompletableFuture<String> future = asyncService.asyncMethod();
        
        String result = future.get(5, TimeUnit.SECONDS);
        assertEquals("Async result", result);
    }
}

์ด ํ…Œ์ŠคํŠธ์—์„œ๋Š” ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ์˜ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ์–ด. CompletableFuture๋ฅผ ์‚ฌ์šฉํ•ด ์ตœ๋Œ€ 5์ดˆ ๋™์•ˆ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋„๋ก ์„ค์ •ํ–ˆ์ง€. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋น„๋™๊ธฐ ์ž‘์—…๋„ ์•ˆ์ „ํ•˜๊ฒŒ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์–ด!

๐Ÿ’ก ๊ฟ€ํŒ: ๋น„๋™๊ธฐ ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ๋•Œ๋Š” ํƒ€์ž„์•„์›ƒ ์„ค์ •์— ์ฃผ์˜ํ•ด์•ผ ํ•ด. ๋„ˆ๋ฌด ์งง๊ฒŒ ์„ค์ •ํ•˜๋ฉด ํ…Œ์ŠคํŠธ๊ฐ€ ๋ถˆ์•ˆ์ •ํ•ด์งˆ ์ˆ˜ ์žˆ๊ณ , ๋„ˆ๋ฌด ๊ธธ๊ฒŒ ์„ค์ •ํ•˜๋ฉด ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ๊ฐ„์ด ๋ถˆํ•„์š”ํ•˜๊ฒŒ ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์–ด. ์ ์ ˆํ•œ ํƒ€์ž„์•„์›ƒ ๊ฐ’์„ ์ฐพ๋Š” ๊ฒŒ ์ค‘์š”ํ•ด!

6. @SpringBootTest ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ โš ๏ธ

@SpringBootTest๋Š” ์ •๋ง ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ง€๋งŒ, ๊ทธ๋งŒํผ ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. ์—ฌ๊ธฐ ๋ช‡ ๊ฐ€์ง€ ์ฃผ์˜์‚ฌํ•ญ์„ ์ •๋ฆฌํ•ด๋ดค์–ด.

6.1 ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ๊ฐ„

@SpringBootTest๋Š” ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ๋ฅผ ๋กœ๋“œํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ๊ฐ„์ด ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์–ด. ํŠนํžˆ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก ์ด ๋ฌธ์ œ๋Š” ๋” ์‹ฌ๊ฐํ•ด์งˆ ์ˆ˜ ์žˆ์ง€.

ํ•ด๊ฒฐ์ฑ…: ๊ฐ€๋Šฅํ•œ ํ•œ ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ(@WebMvcTest, @DataJpaTest ๋“ฑ)๋ฅผ ํ™œ์šฉํ•˜๊ณ , ๊ผญ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ @SpringBootTest๋ฅผ ์‚ฌ์šฉํ•ด. ๋˜ํ•œ, ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ๊ฐ„์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์•„.

6.2 ํ…Œ์ŠคํŠธ ๊ฒฉ๋ฆฌ

@SpringBootTest๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๊ฑฐ์˜ ๋™์ผํ•œ ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํ–‰๋ผ. ์ด๋Š” ํ…Œ์ŠคํŠธ ๊ฐ„ ๊ฒฉ๋ฆฌ๊ฐ€ ์ œ๋Œ€๋กœ ๋˜์ง€ ์•Š์œผ๋ฉด ํ•œ ํ…Œ์ŠคํŠธ์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ด์•ผ.

ํ•ด๊ฒฐ์ฑ…: @Transactional ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด ๊ฐ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๊ฐ€ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์‹คํ–‰๋˜๊ณ  ๋กค๋ฐฑ๋˜๋„๋ก ํ•ด. ๋˜ํ•œ, ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ „ํ›„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ •๋ฆฌํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์•„.

6.3 ์™ธ๋ถ€ ์˜์กด์„ฑ

์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ™˜๊ฒฝ์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋ฉด, ์™ธ๋ถ€ ์„œ๋น„์Šค๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ™์€ ์™ธ๋ถ€ ์˜์กด์„ฑ์— ์˜ํ–ฅ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด. ์ด๋Š” ํ…Œ์ŠคํŠธ์˜ ์•ˆ์ •์„ฑ๊ณผ ์žฌํ˜„์„ฑ์„ ํ•ด์น  ์ˆ˜ ์žˆ์ง€.

ํ•ด๊ฒฐ์ฑ…: ๊ฐ€๋Šฅํ•œ ํ•œ ์™ธ๋ถ€ ์˜์กด์„ฑ์„ ๋ชจํ‚นํ•˜๊ฑฐ๋‚˜, ์ธ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด. @MockBean์„ ํ™œ์šฉํ•˜๊ฑฐ๋‚˜, ํ…Œ์ŠคํŠธ์šฉ ์„ค์ • ํŒŒ์ผ์„ ๋ณ„๋„๋กœ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ.

6.4 ์„ค์ • ์ถฉ๋Œ

์—ฌ๋Ÿฌ ์„ค์ • ํŒŒ์ผ์ด๋‚˜ ํ”„๋กœํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๋‹ค ๋ณด๋ฉด, ์˜๋„์น˜ ์•Š๊ฒŒ ์„ค์ •์ด ์ถฉ๋Œํ•  ์ˆ˜ ์žˆ์–ด. ์ด๋Š” ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ์˜ ์ผ๊ด€์„ฑ์„ ํ•ด์น  ์ˆ˜ ์žˆ์ง€.

ํ•ด๊ฒฐ์ฑ…: @TestPropertySource๋‚˜ @SpringBootTest์˜ properties ์†์„ฑ์„ ์‚ฌ์šฉํ•ด ํ…Œ์ŠคํŠธ์— ํ•„์š”ํ•œ ์„ค์ •์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•ด. ๋˜ํ•œ, ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค๋งˆ๋‹ค ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœํŒŒ์ผ์„ ๋ช…ํ™•ํžˆ ์ง€์ •ํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์•„.

7. @SpringBootTest ํ™œ์šฉ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค ๐Ÿ†

์ž, ์ด์ œ @SpringBootTest๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค๋ฅผ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ. ์ด๊ฑธ ์ž˜ ๋”ฐ๋ฅด๋ฉด ๋” ์•ˆ์ •์ ์ด๊ณ  ํšจ์œจ์ ์ธ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ!

7.1 ํ…Œ์ŠคํŠธ ๊ณ„์ธต ๊ตฌ์กฐ ๋งŒ๋“ค๊ธฐ

๋ชจ๋“  ํ…Œ์ŠคํŠธ์— @SpringBootTest๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค, ํ…Œ์ŠคํŠธ๋ฅผ ๊ณ„์ธตํ™”ํ•˜๋Š” ๊ฒŒ ์ข‹์•„. ์˜ˆ๋ฅผ ๋“ค๋ฉด:

  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ: ์˜์กด์„ฑ์„ ๋ชจํ‚นํ•˜๊ณ  @SpringBootTest ์—†์ด ์ž‘์„ฑ
  • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ: ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ (@WebMvcTest, @DataJpaTest ๋“ฑ)
  • E2E ํ…Œ์ŠคํŠธ: @SpringBootTest ์‚ฌ์šฉ

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์†๋„๋„ ๋นจ๋ผ์ง€๊ณ , ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ ์›์ธ์„ ํŒŒ์•…ํ•˜๊ธฐ๋„ ์‰ฌ์›Œ์ ธ.

7.2 ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ

ํ…Œ์ŠคํŠธ์šฉ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ด. ์—ฌ๊ธฐ ๋ช‡ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ• ๊ฒŒ:

  • @SQL ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด ํ…Œ์ŠคํŠธ ์ „์— ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•˜๊ณ  ํ…Œ์ŠคํŠธ ํ›„์— ์‚ญ์ œํ•˜๊ธฐ
  • TestDataBuilder ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ๋กœ์ง์„ ์ค‘์•™ํ™”ํ•˜๊ธฐ
  • @DirtiesContext๋ฅผ ์‚ฌ์šฉํ•ด ํ•„์š”ํ•  ๋•Œ๋งŒ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ฆฌ๋กœ๋“œํ•˜๊ธฐ

7.3 ์„ค์ • ๋ถ„๋ฆฌ

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์˜ ์„ค์ •์„ ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ๊ณผ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„. ์˜ˆ๋ฅผ ๋“ค์–ด:


src/
  main/
    resources/
      application.yml
  test/
    resources/
      application-test.yml

์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•˜๊ณ , ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค์—์„œ @ActiveProfiles("test")๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ…Œ์ŠคํŠธ์šฉ ์„ค์ •์„ ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด.

7.4 ํ…Œ์ŠคํŠธ ๊ฐ€๋…์„ฑ ๋†’์ด๊ธฐ

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋„ ๊ฒฐ๊ตญ ์ฝ”๋“œ์•ผ. ๊ฐ€๋…์„ฑ ์žˆ๊ฒŒ ์ž‘์„ฑํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด:

  • Given-When-Then ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•ด ํ…Œ์ŠคํŠธ ๊ตฌ์กฐ๋ฅผ ๋ช…ํ™•ํžˆ ํ•˜๊ธฐ
  • ์˜๋ฏธ ์žˆ๋Š” ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์‚ฌ์šฉํ•˜๊ธฐ (์˜ˆ: shouldCreateUserWhenValidInput)
  • @DisplayName์„ ์‚ฌ์šฉํ•ด ํ…Œ์ŠคํŠธ ์„ค๋ช… ์ถ”๊ฐ€ํ•˜๊ธฐ

7.5 ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ ๊ด€๋ฆฌ

ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ด. JaCoCo ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•ด ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ์ธก์ •ํ•˜๊ณ , ์ ์ง„์ ์œผ๋กœ ๊ฐœ์„ ํ•ด ๋‚˜๊ฐ€๋Š” ๊ฒŒ ์ข‹์•„. ํ•˜์ง€๋งŒ 100% ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ๋ชฉํ‘œ๋กœ ํ•˜๊ธฐ๋ณด๋‹ค๋Š”, ์ค‘์š”ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ค‘์‹ฌ์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒŒ ๋” ํšจ๊ณผ์ ์ด์•ผ.

8. ๊ฒฐ๋ก : @SpringBootTest๋กœ ๋” ๋‚˜์€ ํ…Œ์ŠคํŠธ ์ž‘์„ฑํ•˜๊ธฐ ๐ŸŽ‰

์ž, ์ด์ œ ์šฐ๋ฆฌ๋Š” @SpringBootTest์— ๋Œ€ํ•ด ์ •๋ง ๊นŠ์ด ์žˆ๊ฒŒ ์•Œ์•„๋ดค์–ด. ์ด ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์Šคํ”„๋ง ๋ถ€ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋”์šฑ ์•ˆ์ •์ ์ด๊ณ  ์‹ ๋ขฐ์„ฑ ์žˆ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด. ํ•˜์ง€๋งŒ ๊ธฐ์–ตํ•ด์•ผ ํ•  ๊ฑด, ๋„๊ตฌ๋Š” ๋„๊ตฌ์ผ ๋ฟ์ด๋ผ๋Š” ๊ฑฐ์•ผ. ์ค‘์š”ํ•œ ๊ฑด ์šฐ๋ฆฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋А๋ƒ์ง€!

ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” ํ•ญ์ƒ ์ด๋Ÿฐ ์งˆ๋ฌธ์„ ์Šค์Šค๋กœ์—๊ฒŒ ํ•ด๋ด:

  • ์ด ํ…Œ์ŠคํŠธ๊ฐ€ ์ •๋ง ํ•„์š”ํ•œ๊ฐ€?
  • ์ด ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋–ค ๊ฐ€์น˜๋ฅผ ์ œ๊ณตํ•˜๋Š”๊ฐ€?
  • ์ด ํ…Œ์ŠคํŠธ๋ฅผ ๋” ํšจ์œจ์ ์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์€ ์—†์„๊นŒ?

์ด๋Ÿฐ ์งˆ๋ฌธ๋“ค์„ ๊ณ„์† ๋˜์ง€๋ฉด์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋‹ค ๋ณด๋ฉด, ์–ด๋А์ƒˆ ํ›Œ๋ฅญํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์žˆ๋Š” ์ž์‹ ์„ ๋ฐœ๊ฒฌํ•˜๊ฒŒ ๋  ๊ฑฐ์•ผ.

๋งˆ์ง€๋ง‰์œผ๋กœ, ํ…Œ์ŠคํŠธ๋Š” ๋‹จ์ˆœํžˆ ๋ฒ„๊ทธ๋ฅผ ์žก๋Š” ๋„๊ตฌ๊ฐ€ ์•„๋‹ˆ์•ผ. ํ…Œ์ŠคํŠธ๋Š” ์šฐ๋ฆฌ์˜ ์ฝ”๋“œ์— ๋Œ€ํ•œ ์‚ด์•„์žˆ๋Š” ๋ฌธ์„œ์ด๊ณ , ๋ฆฌํŒฉํ† ๋ง์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ์•ˆ์ „๋ง์ด๋ฉฐ, ๋” ๋‚˜์€ ์„ค๊ณ„๋ฅผ ์ด๋Œ์–ด๋‚ด๋Š” ๋„๊ตฌ์•ผ. @SpringBootTest๋ฅผ ์ž˜ ํ™œ์šฉํ•ด์„œ ์ด ๋ชจ๋“  ์ด์ ์„ ๋ˆ„๋ฆฌ๊ธธ ๋ฐ”๋ผ!

์ž, ์ด์ œ ๋„ˆํฌ ์ฐจ๋ก€์•ผ. ๊ฐ€์„œ ๋ฉ‹์ง„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ด. ํ™”์ดํŒ…! ๐Ÿ‘Š๐Ÿ˜„

๐ŸŒŸ ๊ธฐ์–ตํ•˜์„ธ์š”: "ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ๊ฐ€ ์ข‹์€ ์ฝ”๋“œ๋‹ค." ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ๋А๋ผ๋Š” ๋ถˆํŽธํ•จ์€ ์ข…์ข… ์ฝ”๋“œ ์„ค๊ณ„์˜ ๋ฌธ์ œ๋ฅผ ๋“œ๋Ÿฌ๋‚ด๋Š” ์‹ ํ˜ธ์ผ ์ˆ˜ ์žˆ์–ด. ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๋” ๋‚˜์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ธธ ๋ฐ”๋ž„๊ฒŒ!

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.