๐ŸŒˆ [Section3] 10. [ Spring MVC ] ํ…Œ์ŠคํŒ… 1

ํ˜„์ฃผยท2022๋…„ 11์›” 9์ผ
0

bootcamp

๋ชฉ๋ก ๋ณด๊ธฐ
50/71

๐Ÿ“• ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ!

  • ํ…Œ์ŠคํŠธ (Test)
  • JUnit
  • Hamcrest
  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ / ๊ณ„์ธต๋ณ„ ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ ์‹ค์Šต

โœ๏ธ ํ…Œ์ŠคํŠธ (Test)

  • ์–ด๋–ค ๋Œ€์ƒ์— ๋Œ€ํ•œ ์ผ์ • ๊ธฐ์ค€์„ ์ •ํ•ด๋†“๊ณ , ๊ทธ ๋Œ€์ƒ์ด ์ •ํ•ด์ง„ ๊ธฐ์ค€์— ๋ถ€ํ•ฉํ•˜๋Š”์ง€ ๋ถ€ํ•ฉํ•˜์ง€ ๋ชปํ•˜๋Š”์ง€๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ๊ณผ์ •

  • JUnit ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ํ…Œ์ŠคํŒ…

โœ” ํ…Œ์ŠคํŠธ์˜ ์ข…๋ฅ˜

โœ”๏ธ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ

  • ์•„์ฃผ ์ž‘์€ ๋‹จ์œ„์˜ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ๋ฐฉ๋ฒ•

  • ๋Œ€๋ถ€๋ถ„ ๋ฉ”์„œ๋“œ ๋‹จ์œ„์˜ ์ฝ”๋“œ๋กœ ์ž‘์„ฑ

  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ œ์ผ ์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„
    โžœ ํ—ฌํผ(helper) ํด๋ž˜์Šค or ์œ ํ‹ธ๋ฆฌํ‹ฐ(utility) ํด๋ž˜์Šค

    ๐Ÿ’ก But, ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋“ค์˜ ๋ฉ”์„œ๋“œ๋“ค์€ ํด๋ž˜์Šค์˜ ๊ฐ์ฒด๋กœ ์ธ์Šคํ„ด์Šคํ™” ๋  ํ•„์š”๊ฐ€ ์—†์–ด ๋ชจ๋‘ ์ •์ (static)๋ฉ”์„œ๋“œ๋กœ ๊ตฌ์„ฑ
    โžœ ๊ฐ์ฒด ์ง€ํ–ฅ์ ์ธ ์‹œ๊ฐ์œผ๋กœ ์‚ฌ์šฉ ์ง€์–‘ But, ๋งŽ์ด ์‚ฌ์šฉํ•จ

  • ๋‹จ์œ„ํ…Œ์ŠคํŠธ ์žฅ์ 

    • ๊ตฌํ˜„ ์ฝ”๋“œ๊ฐ€ ์˜๋„๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ๋น ๋ฅด๊ฒŒ ๊ฒฐ๊ณผ ํ™•์ธ ๊ฐ€๋Šฅ

    • ๋ฒˆ๊ฑฐ๋กœ์šด ํ…Œ์ŠคํŠธ ์ผ๋“ค ๋‹จ์ˆœํ™”

    • ์ž‘์€ ๋‹จ์œ„๋กœ ๋ฏธ๋ฆฌ ๋ฒ„๊ทธ๋ฅผ ์ฐพ์•„๋‚ผ ์ˆ˜ ์žˆ์–ด ์ƒ๋Œ€์ ์œผ๋กœ ๋” ์ ์€ ์‹œ๊ฐ„ ๋‚ด์— ๋ฌธ์ œ ์ฐพ๊ธฐ ๊ฐ€๋Šฅ

๐Ÿ’ฌ ๊ทธ๋Ÿผ DB๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ผ๊ณ  ๋ณด๊ธฐ ํž˜๋“ค๊นŒ?
โžœ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ์ตœ๋Œ€ํ•œ ๋…๋ฆฝ์ ์ด๊ณ  ์ž‘์€ ๋‹จ์œ„์ธ ๊ฒƒ์ด ์ข‹์ง€๋งŒ,
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ƒํƒœ๊ฐ€ ํ…Œ์ŠคํŠธ ์ด์ „๊ณผ ์ดํ›„๊ฐ€ ๋™์ผํ•˜๊ฒŒ ์œ ์ง€๋  ์ˆ˜ ์žˆ๋‹ค๋ฉด, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์—ฐ๋™๋œ๋‹ค๊ณ  ํ•ด๋„ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์— ํฌํ•จ๋  ์ˆ˜๋Š” ์žˆ์Œ

โœ”๏ธ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€๋ฅผ ํ…Œ์ŠคํŠธ

  • ๊ฐœ๋ฐœํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์—ฐ๊ด€๋œ ๋Œ€์ƒ์ด ๋งŽ์•„ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ผ ๋ถ€๋ฅด๊ธฐ ํž˜๋“ฆ

  • ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋Š” ์ฃผ์ฒด๊ฐ€ ์ฃผ๋กœ ๊ฐœ๋ฐœ์ž ์ด์™ธ์˜ ์ œ 3์ž
    Ex. ํ…Œ์ŠคํŠธ ์ „๋ฌธ ๋ถ€์„œ(QA ๋ถ€์„œ), ์™ธ๋ถ€ QA ์—…์ฒด ๋“ฑ

โœ”๏ธ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ

  • ํด๋ผ์ด์–ธํŠธ ์ธก ํˆด ์—†์ด, ๊ฐœ๋ฐœ์ž๊ฐ€ ์งœ ๋†“์€ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œ์ผœ ํ•˜๋Š” ํ…Œ์ŠคํŠธ

  • ์—ฌ๋Ÿฌ ๊ณ„์ธต๊ณผ ์—ฐ๊ด€๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ผ ๋ถ€๋ฅด๊ธฐ ํž˜๋“ฆ

โœ”๏ธ ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํŠน์ • ๊ณ„์ธต์œผ๋กœ ์ชผ๊ฐœ์„œ ํ•˜๋Š” ํ…Œ์ŠคํŠธ
    Ex. API ๊ณ„์ธต / ์„œ๋น„์Šค ๊ณ„์ธต / ๋ฐ์ดํ„ฐ ์—‘์„ธ์Šค ๊ณ„์ธต

  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ผ๊ณ  ๋ถ€๋ฅด๊ธฐ์—” ๋‹จ์œ„๊ฐ€ ํฐ ํ…Œ์ŠคํŠธ

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ผ๋ถ€๋งŒ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ถ€๋ถ„ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ผ๊ณ  ๋ถ€๋ฅด๊ธฐ๋„ ํ•จ

โœ”๏ธ ์Šค๋ชจํฌ ํ…Œ์ŠคํŠธ (Smoke Test)

  • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋Š” ์•„๋‹ˆ์ง€๋งŒ QA ๋ถ€์„œ์—์„œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์ „์ฒด์ ์ธ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ธฐ ์ „, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํŠน์ • ์ˆ˜์ • ์‚ฌํ•ญ์œผ๋กœ ์ธํ•ด ์˜ํ–ฅ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ๋ฒ”์œ„์— ํ•œํ•ด์„œ ์ œํ•œ๋œ ํ…Œ์ŠคํŠธ
    [์ฐธ๊ณ ] https://en.wikipedia.org/wiki/Smoke_testing_(software)

โœ”๏ธ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค (Test Case)
โžœ ํ•˜๋‚˜์˜ ๋‹จ์œ„๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ


โœ๏ธ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ F.I.R.S.T ์›์น™

  • Fast (๋น ๋ฅด๊ฒŒ)
    โžœ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ๋™์ž‘์ด ๋นจ๋ผ์•ผํ•จ

  • Independent (๋…๋ฆฝ์ ์œผ๋กœ)
    โžœ ๊ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ๋…๋ฆฝ์ ์ด์–ด์•ผ ํ•จ
    ( ๊ฒฐ๊ณผ๋„ ๊ฐ๊ฐ์— ์˜ํ•ญ์„ ๋ฏธ์น˜์ง€ ์•Š์•„์•ผ ํ•จ )

  • Repeatable (๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•˜๋„๋ก)
    โžœ ์–ด๋–ค ํ™˜๊ฒฝ์—์„œ๋„ ๋ฐ˜๋ณต ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•ด์•ผํ•จ
    โžœ ์™ธ๋ถ€ ์„œ๋น„์Šค / ์™ธ๋ถ€ ๋ฆฌ์†Œ์Šค๊ฐ€ ์—ฐ๋™๋œ ๊ฒฝ์šฐ ๋™์ผํ•œ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ๋ณด์žฅ X

  • Self-validating (์…€ํ”„ ๊ฒ€์ฆ์ด ๋˜๋„๋ก)
    โžœ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์Šค์Šค๋กœ ๊ฒฐ๊ณผ๊ฐ€ ์„ฑ๊ณต / ์‹คํŒจ ์ธ์ง€๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•จ

  • Timely (์‹œ๊ธฐ ์ ์ ˆํ•˜๊ฒŒ)
    โžœ ํ…Œ์ŠคํŠธํ•˜๋ ค๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์ง์ „์— ์ž‘์„ฑํ•ด์•ผํ•จ (TDD (ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ))
    ( ๊ตฌํ˜„ํ•˜๊ณ ์ž ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๋‹ค ์ž‘์„ฑํ•˜๊ณ  ์‹คํ–‰์‹œํ‚ค์ง€ ์•Š๊ณ  ์งง๊ฒŒ ์ž‘์„ฑํ•˜๊ณ  ๋Œ๋ ค๋ณด๊ณ  ์ ์  ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋Š” ์‹์œผ๋กœ )


โœ๏ธ JUnit ์—†์ด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ

  • Given - When - Then ํ‘œํ˜„ ์Šคํƒ€์ผ
    ( BDD ํ…Œ์ŠคํŠธ ๋ฐฉ์‹์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์šฉ์–ด )

    โœ”๏ธ BDD (Behavior Driven Development)

    • ํ–‰๋™ ์ค‘์‹ฌ ๊ฐœ๋ฐœ
    • ์†Œํ”„ํŠธ์›จ์–ด ํ”„๋กœ์ ํŠธ์—์„œ ๊ฐœ๋ฐœ์ž, ํ’ˆ์งˆ ๋ณด์ฆ ์ „๋ฌธ๊ฐ€ ๋ฐ ๊ณ ๊ฐ ๋‹ด๋‹น์ž ๊ฐ„์˜ ํ˜‘์—…์„ ์žฅ๋ คํ•˜๋Š” ๋ฏผ์ฒฉํ•œ ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค
      [์ฐธ๊ณ ] https://en.wikipedia.org/wiki/Behavior-driven_development
  • Given

    • ํ…Œ์ŠคํŠธํ•˜๊ณ ์ž ํ•˜๋Š” ๋Œ€์ƒ

    • ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ์ „์ œ ์กฐ๊ฑด๋“ค์ด ํฌํ•จ

    • ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์— ์ „๋‹ฌ๋˜๋Š” ์ž…๋ ฅ ๊ฐ’(ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ)๋„ ํฌํ•จ

  • When

    • ํ…Œ์ŠคํŠธ์˜ ๋™์ž‘ ์ง€์ •
  • Then

    • ํ…Œ์ŠคํŠธ์˜ ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ฆ (Assertion)

    • ์˜ˆ์ƒํ•˜๋Š” ๊ฐ’(expected)๊ณผ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ์˜ ๋™์ž‘ ์ˆ˜ํ–‰ ๊ฒฐ๊ณผ(actual) ๊ฐ’์„ ๋น„๊ตํ•ด์„œ ๊ธฐ๋Œ€ํ•œ๋Œ€๋กœ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•˜๋Š”์ง€ ๊ฒ€์ฆ(Assertion)ํ•˜๋Š” ์ฝ”๋“œ๋“ค

โœ”๏ธ Assertion (๊ฒ€์ฆ)

  • ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ˜๋“œ์‹œ ์ฐธ(true)์ด์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋…ผ๋ฆฌ์ ์œผ๋กœ ํ‘œํ˜„ํ•œ ๊ฒƒ

โœ๏ธ JUnit

  • Java ์–ธ์–ด๋กœ ๋งŒ๋“ค์–ด์ง„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•œ ์˜คํ”ˆ ์†Œ์Šค ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ

  • Java์˜ ํ‘œ์ค€ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋ผ๊ณ  ํ•ด๋„ ๊ณผ์–ธ์ด ์•„๋‹˜

  • TestNG๋ผ๋Š” JUnit์˜ ๊ฐ•๋ ฅํ•œ ๊ฒฝ์Ÿ์ž๊ฐ€ ์žˆ๊ธดํ•˜์ง€๋งŒ, Spring Boot์˜ ๋””ํดํŠธ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋Š” JUnit

  • assertXXXX()๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋‹ค์–‘ํ•œ Assertion ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›

  • JUnit์œผ๋กœ ์ž‘์„ฑํ•œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ํ•ญ์ƒ ์ผ์ •ํ•œ ์ˆœ์„œ๋กœ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์‹คํ–‰ X ( ์ˆœ์„œ ๋žœ๋ค )
    โžœ ๊ฐ๊ฐ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์— ๋…๋ฆฝ์„ฑ์ด ๋ณด์žฅ๋˜์–ด์•ผ ํ•จ

    Ex. ํ•œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์ •๋ณด๋ฅผ delete ํ–ˆ๋‹ค๋ฉด, ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ์—๋Š” ์ด deleteํ•œ ํ–‰์œ„๊ฐ€ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋„๋ก ๋‹ค์‹œ ์ฒ˜์Œ์œผ๋กœ ์ดˆ๊ธฐํ™” ๋˜์–ด์•ผ ํ•จ
    โžœ @Transactional ์• ๋„ˆํ…Œ์ด์…˜ ๋ถ™์ž„

๐Ÿ’ก Spring Boot Initializer ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑํ•œ๋‹ค๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ testImplementation >'org.springframework.boot:spring-boot-starter-test' ์Šคํƒ€ํ„ฐ๊ฐ€ ํฌํ•จ๋˜๋ฉฐ, ์ž๋™์œผ๋กœ JUnit๋„ ํฌํ•จ

โœ” JUnit์„ ์‚ฌ์šฉํ•œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ

public class JunitDefaultStructure {
	 @Test
    public void test1() {
        // ํ…Œ์ŠคํŠธ ํ•˜๊ณ ์ž ํ•˜๋Š” ๋Œ€์ƒ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ ๋กœ์ง ์ž‘์„ฑ
    }
    
     @Test
    public void test2() {
        // ํ…Œ์ŠคํŠธ ํ•˜๊ณ ์ž ํ•˜๋Š” ๋Œ€์ƒ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ ๋กœ์ง ์ž‘์„ฑ
    }
    .
    .
    .
}

โžœ ์œ„์™€ ๊ฐ™์ด void ํƒ€์ž…์˜ ๋ฉ”์„œ๋“œ๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ์— @Test ์• ๋„ˆํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•จ

  • @DisplayName("ํ‘œ์‹œํ•  ์ด๋ฆ„") ์• ๋„ˆํ…Œ์ด์…˜์„ ๋ถ™์—ฌ์ฃผ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด
    ํ•ด๋‹น ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ์— ๋‚ด๊ฐ€ ์ ์€ ์ด๋ฆ„์œผ๋กœ ํ‘œ์‹œ๋จ

โ— ํ…Œ์ŠคํŠธ์—์„œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๊ณ  ๋ฌด์กฐ๊ฑด failed๊ฐ€ ์•„๋‹Œ,
์šฐ๋ฆฌ๊ฐ€ ๊ธฐ๋Œ€ํ•œ ๊ฐ’(ํ†ต๊ณผ or ์˜ˆ์™ธ)์ด ๊ฒฐ๊ณผ ๊ฐ’๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€์— ๋”ฐ๋ผ passed / failed๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ž„์„ ์•Œ๊ธฐ !

โœ” Assertion ๋ฉ”์„œ๋“œ

โžœ ํ…Œ์ŠคํŠธ๊ฐ€ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์ œ๋Œ€๋กœ ๋ฆฌํ„ดํ•˜๋Š”์ง€ ์—๋Ÿฌ๋Š” ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”์ง€ ํ™•์ธํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ

โ— ๊ฐœ๋ฐœ์ž๊ฐ€ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ถ์€ ์ธ์ž๊ฐ’์„ ๋„ฃ์—ˆ์„ ๋•Œ ์˜ˆ์ƒํ•œ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค๋Š”์ง€ ํ…Œ์ŠคํŠธ ํ•ด๋ณผ ๊ฒฝ์šฐ ์‚ฌ์šฉ ( ์‚ฌ์‹ค์ž„์„ ์ฃผ์žฅ )

  • assertEquals(๊ธฐ๋Œ€ํ•˜๋Š” ๋ฌธ์ž์—ด, ์‹ค์ œ ๊ฒฐ๊ณผ ๊ฐ’)
    โžœ ๊ธฐ๋Œ€ํ•˜๋Š” ๊ฐ’๊ณผ ์‹ค์ œ ๊ฒฐ๊ณผ ๊ฐ’์ด ์ผ์น˜ํ•˜๋Š”์ง€ ๊ฒ€์ฆ

  • assertTrue(์˜ˆ์ƒํ•  ๋Œ€์ƒ)
    โžœ ๋Œ€์ƒ์ด true์ธ์ง€ ๊ฒ€์ฆ

  • assertFalse(์˜ˆ์ƒํ•  ๋Œ€์ƒ)
    โžœ ๋Œ€์ƒ์ด false์ธ์ง€ ๊ฒ€์ฆ

  • assertNotNull(ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ๊ฐ์ฒด, ํ…Œ์ŠคํŠธ ์‹คํŒจ์‹œ ํ‘œ์‹œํ•  ๋ฉ”์„ธ์ง€)
    โžœ Null์ด ์•„๋‹Œ์ง€ ์—ฌ๋ถ€ ํ…Œ์ŠคํŠธ
    ( Null์ด ์•„๋‹ˆ๋ฉด passed / Null์ด๋ฉด failed )

  • assertNull(ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ๊ฐ์ฒด, ํ…Œ์ŠคํŠธ ์‹คํŒจ์‹œ ํ‘œ์‹œํ•  ๋ฉ”์„ธ์ง€)
    โžœ Null์ธ์ง€ ์—ฌ๋ถ€ ํ…Œ์ŠคํŠธ
    ( Null์ด๋ฉด passed / Null์ด ์•„๋‹ˆ๋ฉด failed )

  • assertThrows(๋ฐœ์ƒ์ด๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋˜๋Š” ์˜ˆ์™ธ ํด๋ž˜์Šค, ๋žŒ๋‹ค์‹์œผ๋กœ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ)
    โžœ ์˜ˆ์ƒํ•œ ์˜ˆ์™ธ(Exception)๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ํ…Œ์ŠคํŠธ

Ex.

@Test
public void assertionThrowExceptionTest() {
		assertThrows(NullPointerException.class, () -> getColor("RED"));
}
โ €
private String getColor(String unit) {
        return Color.map.get(unit).toUpperCase();
}

์œ„์˜ ์˜ˆ์‹œ์—์„œ RED๋ผ๋Š” ์ƒ‰์ด ์—†์œผ๋ฉด assertThrows()๋Š” null์ด๋ผ๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ๊ฒƒ.
๊ทธ๋Ÿฐ๋ฐ ๋ฐ‘์—์„œ null์ธ ๊ฐ’์— toUpperCase() ๋ฉ”์„œ๋“œ๋กœ ๋Œ€๋ฌธ์ž๋กœ ๋ณ€ํ™˜ํ•˜๋ ค๊ณ  ํ•˜๊ธฐ ๋•Œ๋ฌธ์—
๊ฒฐ๊ตญ์€ NullPointerException์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋จ
โžœ ์šฐ๋ฆฌ๊ฐ€ ์˜ˆ์ƒํ•œ ์˜ˆ์™ธ ๊ฐ’์ด NullPointerException์ด ๋งž๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ passed
โ €
โ— assertThrows() ๋ฉ”์„œ๋“œ ๋‚ด์˜ ExpectedType ์„ ์ƒ์œ„ ํƒ€์ž…์ž…์ธ RuntimeException.class ์ด๋‚˜ Exception.class ์œผ๋กœ ์ž…๋ ฅํ•ด๋„ ๊ฒฐ๊ณผ๋Š” passed !
( NullPointerException extends RuntimeException / RuntimeException extends Exception )
โžœ ์˜ˆ์™ธ ํด๋ž˜์Šค์˜ ์ƒ์† ๊ด€๊ณ„๋ฅผ ์ดํ•ดํ•œ ์ƒํƒœ์—์„œ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๊ฒฐ๊ณผ ์˜ˆ์ƒํ•ด์•ผํ•จ

[์ฐธ๊ณ ] https://beomseok95.tistory.com/205
[Assertions ์ฐธ๊ณ ] https://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions

โœ” Assumption ๋ฉ”์„œ๋“œ

โžœ ๋ฉ”์†Œ๋“œ๋ณ„ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•  ๊ฒฝ์šฐ ์ง„ํ–‰์‹œํ‚ค๊ณ  ์•„๋‹Œ ๊ฒฝ์šฐ ์Šคํ‚ตํ•˜๋Š” ๋ฉ”์†Œ๋“œ

โ— ๊ฐœ๋ฐœ์ž๊ฐ€ ์ธ์ž๊ฐ’์„ ์ •ํ™•ํžˆ ๋ชจ๋ฅผ ๋•Œ if ์™€ ๊ฐ™์€ ์šฉ๋„๋กœ ์‚ฌ์šฉ ( ๊ฐ€์ • / ์ถ”์ • )

  • assumeTrue()
    โžœ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž…๋ ฅ๋œ ๊ฐ’์ด true์ด๋ฉด ๋‚˜๋จธ์ง€ ์•„๋ž˜ ๋กœ์ง๋“ค์„ ์‹คํ–‰

  • assumeFalse()
    โžœ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž…๋ ฅ๋œ ๊ฐ’์ด false์ด๋ฉด ๋‚˜๋จธ์ง€ ์•„๋ž˜ ๋กœ์ง๋“ค์„ ์‹คํ–‰

[Assunptions ์ฐธ๊ณ ] https://junit.org/junit5/docs/current/user-guide/#writing-tests-assumptions
[Assertion / Assumption ์ฐจ์ด ์ฐธ๊ณ ] https://effortguy.tistory.com/123

โœ” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์‹คํ–‰ ์ „, ์ „์ฒ˜๋ฆฌ

  • @BeforeEach

    • ํด๋ ˆ์Šค ๋ ˆ๋ฒจ์— ์‚ฌ์šฉ

    • ๊ฐ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๊ฐ€ ์‹คํ–‰๋  ๋•Œ ๋งˆ๋‹ค ์ด ์• ๋„ˆํ…Œ์ด์…˜์„ ๋ถ™์ธ ๋ฉ”์„œ๋“œ์˜ ๋‚ด์šฉ๋“ค์ด ์‹คํ–‰๋œ ํ›„, ํ•ด๋‹น ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์‹คํ–‰
      โ €

      Ex.

      public class BeforeEachTest {
          private Map<String, String> map;
      โ €
          @BeforeEach
          public void init() {
              map = new HashMap<>();
              map.put("RED", "Red");
              map.put("BLA", "Black");
          }
      โ €
          @DisplayName("Test case 1")
          @Test
          public void beforeEachTest1() {
              map.put("WHI", "White");
              assertDoesNotThrow(() -> getCryptoCurrency("WHI"));
              // ๊ฒฐ๊ณผ --> passed ( Assertion ํ•˜๊ธฐ ์ „์— map ์— โ€œXRPโ€๋ฅผ ์ถ”๊ฐ€ํ–ˆ๊ธฐ ๋•Œ๋ฌธ )
          }
      โ €
          // Test case 2 ์‹คํ–‰๋˜๊ธฐ ์ „, init() ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด์„œ map ๊ฐ์ฒด ๋‹ค์‹œ ์ดˆ๊ธฐํ™”
          // --> ์•ž ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์—์„œ WHI ์ถ”๊ฐ€ ํ–ˆ๋”๋ผ๋„ ๋‹ค์‹œ ์ดˆ๊ธฐํ™”๋˜์–ด ์ด์ „ ์ƒํƒœ๋กœ ๋˜๋Œ์•„๊ฐ
          @DisplayName("Test case 2")
          @Test
          public void beforeEachTest2() {
              System.out.println(map);
              assertDoesNotThrow(() -> getColor("WHI"));
              // ๊ฒฐ๊ณผ --> failed
              // ์˜ˆ์™ธ ๋ฐœ์ƒํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ๊ณ  ๊ธฐ๋Œ€ํ–ˆ๋Š”๋ฐ, Assertion ํ•˜๊ธฐ ์ „์—
              // map ์— โ€œWHIโ€๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•„์„œ ์•ˆ์— ๊ฐ’์ด ์—†์–ด์„œ ์˜ˆ์™ธ ๋ฐœ์ƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ
          }
      โ €
          private String getColor(String unit) {
              return map.get(unit).toUpperCase();
          }
      }
  • @BeforeAll()
    • ํด๋ ˆ์Šค ๋ ˆ๋ฒจ์— ์‚ฌ์šฉ
    • ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ํ•œ๊บผ๋ฒˆ์— ์‹คํ–‰ ์‹œํ‚ค๋ฉด, ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋“ค์ด ์‹คํ–‰๋˜๊ธฐ ์ „์— ๋”ฑ ํ•œ๋ฒˆ๋งŒ ์ด ์• ๋„ˆํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ์˜ ๋‚ด์šฉ๋“ค์ด ์‹คํ–‰๋œ ํ›„ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋“ค์ด ์‹คํ–‰
    • ์ด ์• ๋„ˆํ…Œ์ด์…˜์ด ํฌํ•จ๋œ ๋ฉ”์„œ๋“œ๋Š” ๊ผญ ์ •์ (static)๋ฉ”์„œ๋“œ
      โ €โ €

      Ex.

      public class BeforeAllTest {
          private static Map<String, String> map;
      โ €
          @BeforeAll // ์ด ์• ๋„ˆํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•œ ๋ฉ”์„œ๋“œ๋Š” ์ •์ (static) ๋ฉ”์„œ๋“œ์—ฌ์•ผ ํ•จ !!
          public static void initAll() {
              map = new HashMap<>();
              map.put("RED", "Red");
              map.put("BLA", "Black");
      โ €โ €
              System.out.println("initialize Color map");
              // ๊ฒฐ๊ณผ --> ๋‘ ์ผ€์ด์Šค ํ†ตํ‹€์–ด์„œ ์ดˆ๊ธฐํ™”๊ฐ€ ํ•œ๋ฒˆ๋งŒ ๋˜๊ธฐ ๋•Œ๋ฌธ์—
              // "initialize Color map" ํ•œ๋ฒˆ ์ถœ๋ ฅ
          }
      โ €
          @DisplayName("Test case 1")
          @Test
          public void beforeEachTest1() {
          	map.put("WHI", "White");
              assertDoesNotThrow(() -> getColor("WHI"));
          }
      โ €
          @DisplayName("Test case 2")
          @Test
          public void beforeEachTest2() {
              assertDoesNotThrow(() -> getColor("WHI"));
              // ๊ฒฐ๊ณผ --> passed ์ดˆ๊ธฐํ™”๊ฐ€ ํ…Œ์ŠคํŠธ1 ์ง„ํ–‰ ์ „์— ํ•œ๋ฒˆ ๋˜๊ธฐ ๋•Œ๋ฌธ์—,
              // test1์—์„œ ์ €์žฅํ•œ ๊ฐ’์ด ์‚ฌ๋ผ์ง€์ง€ ์•Š๊ณ  ๋‚จ๊ฒŒ ๋˜์–ด ์กฐํšŒ ๊ฐ€๋Šฅ
          }
      โ €
          private String getColor(String unit) {
              return map.get(unit).toUpperCase();
          }
      }

โœ” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์‹คํ–‰ ํ›„, ํ›„์ฒ˜๋ฆฌ

  • @AfterEach

  • @AfterAll

( ์œ„ ์ „์ฒ˜๋ฆฌ ์• ๋„ˆํ…Œ์ด์…˜์ด๋ž‘ ๋™์ž‘๋ฐฉ์‹์€ ๊ฐ™๊ณ , ํ˜ธ์ถœ๋˜๋Š” ์‹œ์ ๋งŒ ๋ฐ˜๋Œ€ )


โœ๏ธ Hamcrest

  • JUnit ๊ธฐ๋ฐ˜์˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Assertion Framework

  • Assertion์„ ์œ„ํ•œ ๋งค์ณ(Matcher)์˜ ์ œ๊ณต์œผ๋กœ ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ํŽธํ•œ ์ž์—ฐ์Šค๋Ÿฌ์šด Assertion ๋ฌธ์žฅ ๊ตฌ์„ฑ
    โžœ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ

  • ํ…Œ์ŠคํŠธ ์‹คํŒจ ๋ฉ”์‹œ์ง€๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์›€

โžœ ์œ„์˜ ์žฅ์ ๋“ค๋•Œ๋ฌธ์— JUnit์— ์ง€์›ํ•˜๋Š” Assertion ๋ฉ”์„œ๋“œ ๋ณด๋‹ค ๋” ๋งŽ์ด ์‚ฌ์šฉ

๐Ÿ’ก But, ์˜ˆ์™ธ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋Š” Hamcrest ๋งŒ์œผ๋กœ Assertion ๊ตฌ์„ฑ์ด ํž˜๋“ฆ
โžœ JUnit ์˜ assetThrows() ๋ฉ”์„œ๋“œ ์ด์šฉ
โ €

  • Hamcrest ๋งŒ์œผ๋กœ ๋˜์ ธ์ง„(thrown) ์˜ˆ์™ธ๋ฅผ ํ…Œ์ŠคํŠธ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด,
    Custom Matcher๋ฅผ ์ง์ ‘ ๊ตฌํ˜„ํ•ด์„œ ์‚ฌ์šฉ

[์ฐธ๊ณ ] http://hamcrest.org/JavaHamcrest/tutorial

โœ” Hamcrest์—์„œ ์ง€์›ํ•˜๋Š” ๋งค์ณ(Matcher)

โœ”๏ธ assertThat( ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์˜ ์‹ค์ œ ๊ฒฐ๊ณผ๊ฐ’, ์˜ˆ์ƒํ•˜๋Š” ๊ฐ’ )

  • assertEquals()๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํ•˜๋‚˜์˜ ์˜์–ด ๋ฌธ์žฅ์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ฝํ˜€ ๋” ๊ตฌ์ฒด์ ์ธ ์˜๋ฏธ ์œ ์ถ” ๊ฐ€๋Šฅ
  • ์‹คํ–‰ ๊ฒฐ๊ณผ ๋˜ํ•œ ๋” ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ฝํž˜
  • is()
    โžœ ๊ฐ€๋…์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•œ decorator

  • equalTo()
    โžœ ๊ฐ์ฒด๊ฐ€ ๋™์ผํ•œ์ง€ ํŒ๋‹จ

    Ex. ( ์•„๋ž˜ ์„ธ ์˜ˆ์‹œ๋Š” ๋™์ผํ•จ )

    • assertThat(theBiscuit, equalTo(myBiscuit));
    • assertThat(theBiscuit, is(equalTo(myBiscuit)));
    • assertThat(theBiscuit, is(myBiscuit));
  • hasToString()
    โžœ ๋ฉ”์„œ๋“œ ๊ฐ’๊ณผ ์ผ์น˜ ์—ฌ๋ถ€ ํŒ๋‹จ

  • notNullValue() /nullValue()
    โžœ Null์ธ์ง€ ์•„๋‹Œ์ง€ ํŒ๋ณ„

[์ฐธ๊ณ ] http://hamcrest.org/JavaHamcrest/javadoc/2.2/
[Matcher ์ข…๋ฅ˜ ์ฐธ๊ณ ] https://velog.io/@hanblueblue/spring-boot-%EC%9C%A0%EB%8B%9B-%ED%85%8C%EC%8A%A4%ED%8A%B8-4.Hamcrest


โœ๏ธ ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ

  • ๊ฐ ๊ณ„์ธต์— ๊ตฌํ˜„ํ•ด ๋†“์€ ๊ธฐ๋Šฅ๋“ค์ด ์ž˜ ๋™์ž‘ํ•˜๋Š”์ง€ ํŠน์ • ๊ณ„์ธต๋งŒ ์ž˜๋ผ์„œ(Slice) ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ

โœ” API ๊ณ„์ธต ํ…Œ์ŠคํŠธ

  • API ๊ณ„์ธต์˜ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ - Controller

โœ”๏ธ Controller ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ์˜ ๋ชฉ์  !!

  • ๊ตฌ์ฒด์ ์ธ ๋กœ์ง์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•จ์ด ์•„๋‹Œ,
    HTTP request๋ถ€ํ„ฐ response ๊ตฌ๊ฐ„๊นŒ์ง€ ์ด์ƒ์—†์ด ๋™์ž‘ํ•˜๊ฑฐ DTO ์œ ํšจ์„ฑ ๊ฒ€์ฆ์ด ์ž˜ ์ผ์–ด๋‚˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•จ
    ( ์š”์ฒญ, ์‘๋‹ต์ด ์ž˜ ๋“ค์–ด์˜ค๊ณ  DTO ์œ ํšจ์„ฑ ๊ฒ€์ฆ์ด ์ž˜ ์ผ์–ด๋‚˜๋ƒ )

โœ”๏ธ Controller ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ๊ตฌ์กฐ

@SpringBootTest
@AutoConfigureMockMvc
public class ControllerTestDefaultStructure {
โ €
    @Autowired
    private MockMvc mockMvc;
   โ €  
    @Test
    public void postMemberTest() {
    // ํ…Œ์ŠคํŠธํ•˜๊ณ ์ž ํ•˜๋Š” Controller ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ž‘์„ฑ
       โ €  
        // given
        // ํ…Œ์ŠคํŠธ์šฉ request body ์ƒ์„ฑ
     โ €   
        // when
        // MockMvc ๊ฐ์ฒด๋กœ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ Controller ํ˜ธ์ถœ
        // ( MockMvc ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์š”์ฒญ URI / HTTP ๋ฉ”์„œ๋“œ๋“ฑ ์ง€์ •ํ•˜๊ณ , given ์—์„œ ๋งŒ๋“ 
        // ํ…Œ์ŠคํŠธ์šฉ request body ๋ฅผ ์ถ”๊ฐ€ํ•œ ๋’ค, request ์ˆ˜ํ–‰ )
       โ € 
        // then
        // Controller ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ์—์„œ ์‘๋‹ต์œผ๋กœ ์ˆ˜์‹ ํ•œ HTTP Status / response body ๊ฒ€์ฆ 
    }
}
  • @SpringBootTest

    • Spring Boot ๊ธฐ๋ฐ˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•œ Application Context ์ƒ์„ฑ
      ( Application Context ์—๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ํ•„์š”ํ•œ Bean ๊ฐ์ฒด๋“ค์ด ๋“ฑ๋ก๋˜์–ด ์žˆ์Œ )

    • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์— ์ ํ•ฉ

  • @AutoConfigureMockMvc

    • Controller ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ž๋™ ๊ตฌ์„ฑ ์ž‘์—…์„ ํ•ด์คŒ
      ( ํ…Œ์ŠคํŠธ์— ํ•„์š”ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ตฌ์„ฑ์ด ์ž๋™์œผ๋กœ ์ง„ํ–‰ )
  • MockMvc

    • Tomcat ๊ฐ™์€ ์„œ๋ธŒ๋ฆฟ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  Spring ๊ธฐ๋ฐ˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ Controller ๋ฅผ ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ๋Š” ์™„๋ฒฝํ•œ ํ™˜๊ฒฝ์„ ์ง€์›ํ•ด์ฃผ๋Š” ์ผ์ข…์˜ Spring MVC ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ

    • @AutoConfigureMockMvc ์• ๋„ˆํ…Œ์ด์…˜์„ ๊ผญ ๋‹ฌ์•„์•ผ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

      [์ฐธ๊ณ ] https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#spring-mvc-test-framework

โœ”๏ธ @WebMvcTest๋ฅผ ์ด์šฉํ•œ Controller ํ…Œ์ŠคํŠธ

  • Controller๋ฅผ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•œ ์ „ํ†ต์ ์ธ ๋ฐฉ๋ฒ•
  • ์›น ๊ณ„์ธต ์ „์šฉ ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ์— ์ ํ•ฉ
  • ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋นˆ๋“ค๋งŒ ๋“ฑ๋กํ•ด์„œ ์‚ฌ์šฉ
    โ €
    But, Controller์—์„œ ์˜์กดํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์„ ๋ชจ๋‘ ์ผ์ผ์ด ์„ค์ •ํ•ด์ค˜์•ผํ•˜๊ณ ,
    ๋•Œ์— ๋”ฐ๋ผ์„œ ๋ฐ์ดํ„ฐ์•ก์„ธ์Šค ๊ณ„์ธต์—์„œ ์˜์กดํ•˜๋Š” ์„ค์ •/์˜์กด ๊ฐ์ฒด๋“ค๋„ ๋ชจ๋‘ ์„ค์ •ํ•ด ์ฃผ์–ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ๋Š” ๋ถˆํŽธํ•จ ์žˆ์Œ
    โ €
    โžœ @SpringBootTest, @AutoConfigureMockMvc๋ฅผ ์ด์šฉํ•ด์„œ Controller ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ๊ตฌ์„ฑ์˜ ๋ณต์žกํ•จ์„ ํ•ด๊ฒฐ
    โ €
    โ— But, ๊ฐ๊ฐ์˜ ์žฅ๋‹จ์ ์ด ์กด์žฌํ•˜๋ฏ€๋กœ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์ ์ ˆํ•˜๊ฒŒ ์‚ฌ์šฉ !

โœ”๏ธ ๊ฐ ๋‹จ๊ณ„์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ ์ •๋ฆฌ

  • ์š”์ฒญ์ˆ˜ํ–‰
    โžœ perform()

  • Http Method, request URL, path variable์„ ์œ„ํ•œ API ๋ฉ”์„œ๋“œ
    โžœ MockMvcRequestBuilders.post(...), MockMvcRequestBuilders.patch(...), MockMvcRequestBuilders.get(...), MockMvcRequestBuilders.delete(...)
    โ €
    โ— MockMvcRequestBuilders - GET, POST, PUT, DELETE ์š”์ฒญ ๋ฐฉ์‹๊ณผ ๋งคํ•‘๋˜๋Š” get(), post(), put(), delete() ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณต

[์ฐธ๊ณ ] https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.html

  • request body๋ฅผ ์œ„ํ•œ API ๋ฉ”์„œ๋“œ
    โžœ content(...)

  • query parameter
    โžœ params(...)

  • response body ๊ฒ€์ฆ
    โžœ andExpect(jsonPath("$.data.~~").value(...))

[์ฐธ๊ณ ]
https://www.baeldung.com/guide-to-jayway-jsonpath
https://scshim.tistory.com/321

โœ” ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๊ณ„์ธต ํ…Œ์ŠคํŠธ

( ์‹ค์Šต์—์„œ ํ˜„์žฌ ๋ฐ์ดํ„ฐ ์—‘์„ธ์Šค ๊ณ„์ธต์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๊ธฐ์ˆ  - Spring Data JPA )

โ— DB์˜ ์ƒํƒœ๋ฅผ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์‹คํ–‰ ์ด์ „์œผ๋กœ ๋˜๋Œ๋ ค์„œ ๊นจ๋—ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์•ผํ•จ
โžœ ๊ฐ๊ฐ์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์— ๋…๋ฆฝ์„ฑ์ด ๋ณด์žฅ๋˜์–ด์•ผ ํ•จ

Ex. DB์— ์ž„์˜์˜ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ทธ๊ฒƒ์ด ์ž˜ ์กฐํšŒ๋˜๋Š”์ง€ ํ…Œ์ŠคํŠธ๋ฅผ ํ–ˆ์—ˆ์œผ๋ฉด,
๊ทธ ์ €์žฅํ•œ ๊ฐ’์€ ๋‹ค์‹œ ์‚ญ์ œํ•ด์ฃผ์–ด DB๋ฅผ ์›๋ž˜๋Œ€๋กœ ๋ณด์กด์‹œ์ผœ๋†“์•„์•ผ ํ•จ

โœ”๏ธ Repository ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ๊ตฌ์กฐ

@DataJpaTest
public class RepositoryTestDefaultStructure {
โ €
    @Autowired
    private MemberRepository memberRepository;
   โ €  
    @Test
    public void saveMemberTest() {
    // ํ…Œ์ŠคํŠธํ•˜๊ณ ์ž ํ•˜๋Š” Controller ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ž‘์„ฑ
       โ €  
        // given
        // ํ…Œ์ŠคํŠธ์šฉ ๋ฐ์ดํ„ฐ ์ค€๋น„ ( ๊ฐ์ฒด์— ๋ฐ์ดํ„ฐ ์ €์žฅ )
     โ €   
        // when
        // Repository์— ๊ฐ์ฒด ์ €์žฅ
        // ์ •๋ณด๋“ค ์ค‘ ํ•˜๋‚˜๋งŒ ๋ฝ‘์•„ ๊ฒ€์ฆํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Repository์˜ findByXXX() ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
       โ € 
        // then
        // ์ •๋ณด๊ฐ€ ์ž˜ ์ €์žฅ๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆ (Assertion)
    }
}
  • @DataJpaTest

    • ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๊ณ„์ธต์„ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ€์žฅ ํ•ต์‹ฌ์ ์ธ ๋ฐฉ๋ฒ•

    • ํ•ด๋‹น Repository์˜ ๊ธฐ๋Šฅ์„ ์ •์ƒ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ Configuration์„ Spring์ด ์ž๋™์œผ๋กœ ํ•ด์คŒ
      ( ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๊ณ„์ธต์— ํ•„์š”ํ•œ ์ž๋™๊ตฌ์„ฑ ํ™œ์„ฑํ™” )

    • @Transactional ์• ๋„ˆํ…Œ์ด์…˜์„ ํฌํ•จํ•˜๊ณ  ์žˆ์Œ

  • @Transactional

    • ํ•˜๋‚˜์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์‹คํ–‰์ด ์ข…๋ฃŒ๋˜๋Š” ์‹œ์ ์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋Š” rollback ์ฒ˜๋ฆฌ๋จ
      ( ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŠธ ์‹คํ–‰์‹œ์ผœ๋„ ํ•˜๋‚˜์˜ ํ…Œ์ŠคํŠธ๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ ๋งˆ๋‹ค DB ๊ฐ€ ์ดˆ๊ธฐ ์ƒํƒœ ์œ ์ง€ )

๐Ÿ˜œ ์‹ค์Šต

  • projects - be-template-testing
  • git - be-homework-testing-junit
  • git - be-homework-testing-slice

........................................................................................................................................................................................................
๐Ÿ’ก ๊ฒฐ๊ณผ๋ฅผ ์ฝ˜์†”์ฐฝ์—์„œ ํ™•์ธํ•  ๋•Œ response body ์‘๋‹ต ๋ฐ์ดํ„ฐ์— ํฌํ•จ๋œ ํ•œ๊ธ€์ด ๊นจ์งˆ ๊ฒฝ์šฐ,
application.yml ํŒŒ์ผ์— ์•„๋ž˜์˜ ์„ค์ •์„ ์ถ”๊ฐ€

server:
  servlet:
    encoding:
      force-response: true

........................................................................................................................................................................................................
But, Controller ํ…Œ์ŠคํŠธ์—์„œ ์˜ค๋Š˜ ์ž‘์„ฑํ•œ ์‹ค์Šต ๋‚ด์šฉ์€ Controller๋งŒ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „์ฒด ๋กœ์ง์„ ๋ชจ๋‘ ์‹คํ–‰ํ•˜๊ฒŒ๋จ

โžœ ์™„์ „ํ•œ ์Šฌ๋ผ์ด์Šค ํ…Œ์ŠคํŠธ๋ผ๊ณ  ๋ณด๊ธฐ์—๋Š” ํž˜๋“ฆ

โžœ ์ด ๋ฌธ์ œ๋Š” Mock(๊ฐ€์งœ) ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด ๊ณ„์ธต ๊ฐ„์˜ ์—ฐ๊ฒฐ์„ ๋Š์–ด์คŒ์œผ๋กœ์จ ํ•ด๊ฒฐ์ด ๊ฐ€๋Šฅ


๐ŸŒˆ ๋Š๋‚€์ 

ํ…Œ์ŠคํŒ…ํ•˜๋Š” ๋ถ€๋ถ„ ํ•™์Šต์€ ์žฌ๋ฐŒ๋Š” ๊ฒƒ ๊ฐ™๋‹ค !
ํ…Œ์ŠคํŒ…์ด ์ž˜ ๋์„ ๋•Œ โœ”๏ธ ์ด ํ‘œ์‹œ๊ฐ€ ๋‚˜์˜ค๋Š” ๊ฒŒ ๋ญ”๊ฐ€ ๋ฟŒ๋“ฏ ..!
์‹ค์Šต์€ ๋‹ค ๊ดœ์ฐฎ์•˜๋Š”๋ฐ getMembers ํ…Œ์ŠคํŒ… ๋ถ€๋ถ„์ด ์–ด๋ ค์› ๋‹ค. ( ์ด๊ฑด ๋‹ค์‹œ ํ•œ๋ฒˆ ๋” ๋ณด๊ธฐ ! )

0๊ฐœ์˜ ๋Œ“๊ธ€