Backend Language/TDD

[TDD Chapter9] ํ…Œ์ŠคํŠธ ๋ฒ”์œ„์™€ ์ข…๋ฅ˜

chaerlo127 2023. 2. 23. 18:50
728x90

๐Ÿฅ€ ํ…Œ์ŠคํŠธ ๋ฒ”์œ„์™€ ์ข…๋ฅ˜

์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฐœ๋ฐœ ์™„๋ฃŒ ํ›„์— ์ง„ํ–‰ํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ผ๊ณ  ๋ถ€๋ฅด๋Š”๋ฐ, ๊ณ ๊ฐ ์ž…์žฅ์—์„œ ์š”๊ตฌํ•œ ๊ธฐ๋Šฅ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋Š”์ง€ ์ˆ˜ํ–‰ํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ์ธ์ˆ˜ ํ…Œ์ŠคํŠธ๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. ์ด์ฒ˜๋Ÿผ, ํ…Œ์ŠคํŠธ์™€ ๊ด€๋ จ ์šฉ์–ด๋Š” ๋ฌธ๋งฅ์ด๋‚˜ ์‚ฌ์šฉ์ž์— ๋”ฐ๋ผ ์˜๋ฏธ๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค.

 

๐ŸŒป ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ(Functional Test) ์™€ E2E ํ…Œ์ŠคํŠธ

์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ ์‹œ์Šคํ…œ์ด ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ์ด ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด์„œ๋Š” ์‹œ์Šคํ…œ์„ ๊ตฌ๋™ํ•˜๊ณ  ์‚ฌ์šฉํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ๋ชจ๋“  ๊ตฌ์„ฑ์š”์†Œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

 

  • ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ์›น ๋ธŒ๋ผ์šฐ์ €๋‚˜ ๋ชจ๋ฐ”์ผ ์•ฑ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ์™ธ๋ถ€ ์„œ๋น„์Šค์— ์ด๋ฅด๊ธฐ๊นŒ์ง€ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ํ•˜๋‚˜๋กœ ์—ฎ์–ด ์ง„ํ–‰
  • ๋ธŒ๋ผ์šฐ์ €(๋) ~ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(๋)๊นŒ์ง€ ๋ชจ๋“  ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์™„์ „ํžˆ ๋…ผ๋ฆฌ์ ์œผ๋กœ ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ์œผ๋กœ ๋‹ค๋ฃธ
  • ์ด์— END TO END (E2E) ํ…Œ์ŠคํŠธ๋ผ๊ณ  ๋ถˆ๋ฆฌ๊ธฐ๋„ ํ•จ.
  • QA ์กฐ์ง์—์„œ ์ฃผ๋กœ ํ•˜๋Š” ํ…Œ์ŠคํŠธ
  • ์‹œ์Šคํ…œ์ด ํ•„์š”ํ•˜๋Š” ๋ฐ์ดํ„ฐ ์ž…๋ ฅํ•˜๊ณ  ๊ฒฐ๊ณผ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธ
  • ์ค‘์š”ํ•œ ๊ฒƒ์€, ์‚ฌ์šฉ์ž์˜ ์ž…์žฅ์—์„œ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ํ™•์ธํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

 

๐ŸŒป ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ

์‹œ์Šคํ…œ์˜ ๊ฐ ๊ตฌ์„ฑ์š”์†Œ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์—ฐ๋™๋˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์‚ฌ์šฉ์ž์˜ ์ž…์žฅ์—์„œ ์ฝ”๋“œ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ, ์ง์ ‘ ์†Œํ”„ํŠธ์›จ์–ด์˜ ์ฝ”๋“œ๋ฅด ์ง์ ‘ ํ…Œ์ŠคํŠธํ•œ๋‹ค. 

 

  • ์ผ๋ฐ˜์ ์ธ ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ํ”„๋ ˆ์ž„์›Œํฌ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ๊ฐ€ ์ฃผ์š” ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ

 

๐ŸŒป ๋‹จ์œ„ ํ…Œ์ŠคํŠธ

๊ฐœ๋ณ„ ์ฝ”๋“œ๋‚˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ธฐ๋Œ€ํ•œ ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ์—ฌํƒœ๊นŒ์ง€ ๋ธ”๋กœ๊ทธ์— ํฌ์ŠคํŒ…ํ•œ ์ฝ”๋“œ๋Š” ์ฃผ๋กœ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ด๋‹ค.

  • ํ•œ ํด๋ž˜์Šค๋‚˜ ํ•œ ๋ฉ”์„œ๋“œ์™€ ๊ฐ™์ด ์ž‘์€ ๋ฒ”์œ„๋ฅผ ํ…Œ์ŠคํŠธํ•œ๋‹ค. 
  • ์ผ๋ถ€ ์˜์กด ๋Œ€์ƒ์€ ์Šคํ…์ด๋‚˜ ๋ชจ์˜ ๊ฐ์ฒด๋“ฑ์„ ์ด์šฉํ•ด์„œ ๋Œ€์—ญ์œผ๋กœ ๋Œ€์ฒดํ•œ๋‹ค. 

 

๐ŸŒป ํ…Œ์ŠคํŠธ ๋ฒ”์œ„๊ฐ„ ์ฐจ์ด

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
DB๋‚˜ ์บ์‹œ ์„œ๋ฒ„์™€ ๊ฐ™์€ ์—ฐ๋™ ๋Œ€์ƒ์„ ๊ตฌ์„ฑ, ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์œ„ํ•ด ๋งŽ์€ ๋…ธ๋ ฅ ์›น ์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•˜๊ฑฐ๋‚˜ ๋ชจ๋ฐ”์ผ ์•ฑ์„ ํฐ์— ์„ค์น˜, ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์œ„ํ•ด ๋งŽ์€ ๋…ธ๋ ฅ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋งŒ ๋นผ๋‘๋ฉด ๋จ
DB ์—ฐ๊ฒฐ, ์†Œ์ผ“ ํ†ต์‹ , ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ ์ดˆ๊ธฐํ™” ๋“ฑ๊ณผ ๊ฐ™์ด ์‹คํ–‰ ์†๋„ ๋Š๋ฆฌ๊ฒŒ ํ•˜๋Š” ์š”์ธ์ด ๋งŽ์Œ ๋ธŒ๋ผ์šฐ์ €๋‚˜ ์•ฑ์„ ๊ตฌ๋™ํ•˜๊ณ  ํ™”๋ฉด์˜ ํ๋ฆ„์— ๋”ฐ๋ผ ์•Œ๋งž์€ ์ƒํ˜ธ์ž‘์šฉ ์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•˜๊ฑฐ๋‚˜ DB๋ฅผ ์ค€๋น„ํ•  ํ•„์š”๊ฐ€ ์—†์Œ, ์˜์กด ๋Œ€์ƒ์„
๋Œ€์—ญ์œผ๋กœ ๋Œ€์ฒด
ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋‚˜ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ๋กœ๋Š” ๊ฒฐ๊ณผ ํ™•์ธ์ด ์–ด๋ ค์šธ ๋•Œ, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์™€ ๋Œ€์—ญ์„ ์กฐํ•ฉํ•˜์—ฌ ์ƒํ™ฉ์„ ๋งŒ๋“ค๊ณ  ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•จ.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋Š” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์„ž์–ด์„œ ์ž‘์„ฑํ•œ๋‹ค. ์–ด๋Š ํ…Œ์ŠคํŠธ๋ฅผ ๋” ๋งŽ์ด ์ž‘์„ฑํ•ด์•ผํ•œ๋‹ค๋Š” ๊ทœ์น™์€ ์—†์ง€๋งŒ, ์ฃผ๋กœ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ๋งŽ์ด ์ž‘์„ฑํ•˜๋Š” ํŽธ์ด๋ผ๊ณ ํ•œ๋‹ค. 

 

๋‹จ์œ„ํ…Œ์ŠคํŠธ๋ฅผ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค๊ณ ๋Š” ํ•˜์ง€๋งŒ, ๊ฐ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์—ฐ๋™๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋Š” ํ•„์š”ํ•˜๋‹ค.

 

๐ŸŒป ํ…Œ์ŠคํŠธ ๋ฒ”์œ„์— ํƒ€๋ฅธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๊ฐœ์ˆ˜์™€ ์‹œ๊ฐ„ 

๊ธฐ๋Šฅ, ํ†ตํ•ฉ, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๋“ฑ ์ „ ๋ฒ”์œ„์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธ๋ฅผ ์ž๋™ํ™”ํ•˜๋Š” ์‹œ๋„๊ฐ€ ์ฆ๊ฐ€ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ๊ณ ํ’ˆ์งˆ์˜ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ๋” ๋น ๋ฅด๊ฒŒ ์ถœ์‹œํ•˜๊ณ ์žํ•˜๋Š” ์ „๋žต์ด ๋Š˜์–ด๋‚˜๊ณ  ์žˆ๋‹ค. 

 

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

 

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

 

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

 

 

๊ธฐ๋Šฅ, ํ†ตํ•ฉ์—์„œ ๋ชจ๋“  ์˜ˆ์™ธ ์ƒํ™ฉ์„ ํ…Œ์ŠคํŠธํ•˜๋ฉด ํ…Œ์ŠคํŠธ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋‚ด์šฉ์ด ์ค‘๋ณต๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ์ค„์–ด๋“ ๋‹ค. ํ…Œ์ŠคํŠธ ์†๋„๋Š” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๊ฐ€ ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ๋“ค๋ณด๋‹ค ์†๋„๊ฐ€ ์›”๋“ฑํžˆ ๋†’๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•˜๋ฉด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์— ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์„ ๋‹ค๋ฃจ๊ณ , ํ†ตํ•ฉ๊ณผ ๊ธฐ๋Šฅ์—์„œ๋Š” ์ฃผ์š” ์ƒํ™ฉ์— ์ดˆ์ ์„ ๋งž์ถฐ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผํ•œ๋‹ค. 

์†๋„๊ฐ€ ๋Š๋ ค์ง€๋ฉด, ์†Œํ”„ํŠธ์›จ์–ด ํ’ˆ์งˆ ์ €ํ•˜๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•˜๋ฉด ๋น ๋ฅธ ์‹œ๊ฐ„ ๋‚ด์— ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผํ•œ๋‹ค.

 

 

๐Ÿฅ€ ์™ธ๋ถ€ ์—ฐ๋™์ด ํ•„์š”ํ•œ ํ…Œ์ŠคํŠธ ์˜ˆ

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

 

๋ชจ๋“  ์™ธ๋ถ€ ์—ฐ๋™ ๋Œ€์ƒ์„ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ ๋‹ค๋ฃฐ ์ˆ˜ ์—†์ง€๋งŒ, ์ผ๋ถ€ ์™ธ๋ถ€ ๋Œ€์ƒ์€ ์–ด๋Š์ •๋„ ์ˆ˜์ค€์—์„œ ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

๐ŸŒป ์Šคํ”„๋ง ๋ถ€ํŠธ์™€ DB ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ

Spring Boot, Spring Data JPA, MySQL ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ–ˆ๋‹ค. 

 

/**
* ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ
*/
@SpringBootTest
@RequiredArgsConstructor
public class UserRegisterTest {

    private final UserRegister register;
    private final JdbcTemplate jdbcTemplate;

    @Test
    void ๋™์ผํ•œ_ID๊ฐ€_์ด๋ฏธ_์กด์žฌํ•˜๋ฉด_์ต์…‰์…˜(){
        // ์ƒํ™ฉ insert ์ฟผ๋ฆฌ ์‹คํ–‰
        jdbcTemplate.update(
                "insert into user values(?, ?, ?)" +
                        "on duplicate key update password = ?, email = ?",
                "cbk", "pw", "cbk@cbk.com", "pw", "cbk@cbk.com");

        // ์‹คํ–‰ ๊ฒฐ๊ณผ ํ™•์ธ
        assertThrows(DupIdException.class, () ->
                register.register("cbk", "strongpw", "email@email.com"));
    }

    @Test
    void ์กด์žฌํ•˜์ง€_์•Š์œผ๋ฉด_์ €์žฅํ•จ(){
        // ์ƒํ™ฉ delete ์ฟผ๋ฆฌ ์‹คํ–‰
        jdbcTemplate.update("delete from user where id = ?", "cbk");

        // ์‹คํ–‰
        register.register("cbk", "strongpw", "email@email.com");

        // ๊ฒฐ๊ณผ ํ™•์ธ select ์ฟผ๋ฆฌ ์ง„ํ–‰
        SqlRowSet rs  = jdbcTemplate.queryForRowSet(
                "select * from user where id = ?", "cbk"
        );

        rs.next();
        assertEquals("email@email.com", rs.getString("email"));
    }

}


/**
* ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
*/
@SpringBootTest
public class UserRegisterTest {
    private UserRegister userRegister;
    private MemoryUserRepository fakeRepository = new MemoryUserRepository();
    // ์ƒ๋žต

    @Test
    void ์ด๋ฏธ_๊ฐ™์€_ID_์กด์žฌํ•˜๋ฉด_๊ฐ€์ž…_์‹คํŒจ(){
        // ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” **๋Œ€์—ญ**์„ ์ด์šฉํ•œ ์ƒํ™ฉ ๊ตฌ์„ฑ
        fakeRepository.save(new User("id", "pw1", "email@email.com"));

        assertThrows(DupIdException.class, () ->{
            userRegister.register("id", "pw2", "email");
        });
    }
}

 

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋Š” ์‹ค์ œ๋กœ DB๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๋™์ผํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆ ์‹คํ–‰ํ•ด๋„ ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™๊ฒŒ ๋‚˜์™€์•ผํ•˜๋ฏ€๋กœ ํ…Œ์ŠคํŠธ์ฝ”๋“œ์—์„œ DB ๋ฐ์ดํ„ฐ๋ฅผ ์•Œ๋งž๊ฒŒ ์ œ์–ดํ•ด์•ผ ํ•œ๋‹ค. 

 

 

๐ŸŒป WireMock์„ ์ด์šฉํ•œ REST ํด๋ผ์ด์–ธํŠธ ํ…Œ์ŠคํŠธ

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์–ด๋ ค์šด ๋Œ€์ƒ์ด ์™ธ๋ถ€ ์„œ๋ฒ„์ด๋‹ค. 

public class CardNumberValidator {

    // 7์žฅ๊ณผ ๋‹ฌ๋ฆฌ ์ถ”๊ฐ€๋œ ์ฝ”๋“œ
    // ์ƒ์„ฑ์ž๋กœ๋ถ€ํ„ฐ server url ์„ ๋ฐ›์•„ ํ•˜๋“œ์ฝ”๋”ฉ์ด ๋˜์ง€ ์•Š๋„๋ก ํ•จ.
    private String server;

    public CardNumberValidator(String server){
        this.server = server;
    }
    //
    public CardValidity validate(String cardNumber){
        HttpClient httpClient = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(server + "/card"))
                .header("Content-Type", "text/plain")
                .POST(HttpRequest.BodyPublishers.ofString(cardNumber))
                .build();
        try{
            HttpResponse<String> response =
                    httpClient.send(request, HttpResponse.BodyHandlers.ofString());
            switch (response.body()){
                case "ok" : return CardValidity.VALID;
                case "bad" : return CardValidity.INVALID;
                case "expired" : return CardValidity.EXPIRED;
                case "theft" : return CardValidity.THEFT;
                default : return CardValidity.UNKNOWNS;
            }
        }catch (HttpTimeoutException e){
            return CardValidity.TIMEOUT;
        }
        catch (IOException | InterruptedException e){
            return CardValidity.ERROR;
        }
    }
}

 

์œ„ ํด๋ž˜์Šค ์ž์ฒด๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋ ค๋ฉด ์ •ํ•ด์ง„ ๊ทœ์น™์— ๋งž๊ฒŒ ํ†ต์‹ ํ•  ์ˆ˜ ์ž‡๋Š” ์„œ๋ฒ„๊ฐ€ ํ•„์š”ํ•œ๋ฐ, ์™ธ๋ถ€์˜ ์นด๋“œ ์ •๋ณด ์ œ๊ณต api์™€ ํ†ต์‹ ํ•  ๋•Œ ์›ํ•˜๋Š” ์ƒํ™ฉ์„ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์—†๋‹ค. ์ด ๋•Œ, ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด WireMock์ด๋‹ค. WireMock์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜ฌ๋ฐ”๋ฅธ ์‘๋‹ต์ด๋‚˜ ํƒ€์ž„์•„์›ƒ๊ณผ ๊ฐ™์€ ์ƒํ™ฉ์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธ ํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

public class CardNumberValidatorTest {
     private WireMockServer wireMockServer;

     @BeforeEach
    void setUp(){
         wireMockServer = new WireMockServer(options().port(8089));
         wireMockServer.start();
     }

     @AfterEach
    void tearDown(){
         wireMockServer.stop();
     }

     @Test
    void valid(){
         wireMockServer.stubFor(post(urlEqualTo("/card")) // url, post ์š”์ฒญ
                 .withRequestBody(equalTo("1234567890")) // ์š”์ฒญ ๋ชธ์ฒด๊ฐ€ "1234567890 ๊ณผ ์š”์ฒญ์ด ๊ฐ™์œผ๋ฉด
                 .willReturn(aResponse()
                         .withHeader("Content-Type", "text/plain") // header๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด
                         .withBody("ok"))); // ์‘๋‹ต ๋ชธ์ฒด๊ฐ€ ok๋กœ ๊ฐ’์„ ์ „๋‹ฌํ•ด์คŒ
         CardNumberValidator validator = new CardNumberValidator("http://localhost:8089");
         CardValidity validity = validator.validate("1234567890");
         assertEquals(CardValidity.VALID, validity);
     }

     @Test
     void timeout(){
         wireMockServer.stubFor(post(urlEqualTo("/card"))
                 .willReturn(aResponse().withFixedDelay(5000)));

         CardNumberValidator validator = new CardNumberValidator("http://localhost:8089");
         CardValidity validity = validator.validate("1234567890");
         assertEquals(CardValidity.TIMEOUT, validity);
     }
}

 

WireMockServer

  • ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ „ ์‹œ์ž‘, ์‹ค์ œ HTTP ์„œ๋ฒ„๊ฐ€ ๋œฌ๋‹ค
  • ํ…Œ์ŠคํŠธ์—์„œ WireMockServer์˜ ๋™์ž‘์„ ๊ธฐ์ˆ 
  •  HTTP ์—ฐ๋™์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ…Œ์ŠคํŠธ ์‹คํ–‰
  • ํ…Œ์ŠคํŠธ ์‹คํ–‰ ํ›„์— WireMockServer ์ค‘์ง€

 

๐ŸŒป ์Šคํ”„๋ง๋ถ€ํŠธ ๋‚ด์žฅ ์„œ๋ฒ„๋ฅผ ์ด์šฉํ•œ API ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ

๋ชจ๋ฐ”์ผ ์•ฑ์—์„œ ํšŒ์› ๊ฐ€์ž…์„ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ํšŒ์› ๊ฐ€์ž… API๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ JSON์„ ์‘๋‹ตํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž. 

ํšŒ์› ๊ฐ€์ž…์€ ๋งค์šฐ ์ค‘์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํšŒ์› ๊ฐ€์ž… API๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ๊ฒ€์ฆ ๊ณผ์ •์„ ์ž๋™ํ™”ํ•˜๋ฉด ๋‹ด๋‹น์ž๊ฐ€ ์ˆ˜๋™์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๋Š” ์‹œ๊ฐ„์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

 

@SpringBootTest(
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
@RequiredArgsConstructor
public class UserApiE2Test {
    private final TestRestTemplate testRestTemplate;

    @Test
    void weakPwResponse(){
        String reqBody = "{\"id\":\"id\", \"pw\":\"123\",\"email\":\"a@a.com\"}";
        RequestEntity<String> request = RequestEntity.post(URI.create("/users"))
                .contentType(PageAttributes.MediaType.APPLICATION_JSON_UTP8)
                .body(reqBody);
        
        ResponseEntity<String> response = testRestTemplate.exchange(
                request, String.class
        );
        
        assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode());
        assertTrue(response.getBody().contains("WeakPasswordException"));
    }
}

 

TestRestTemplate์€ ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ ํ…Œ์ŠคํŠธ ๋ชฉ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์œผ๋กœ์„œ, ๋‚ด์žฅ ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜๋Š” RestTemplate์ด๋‹ค. ์ž„์˜์˜ ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด์žฅ์„œ๋ฒ„๋ฅผ ๊ตฌ๋™ํ•˜๋„๋ก ์„ค์ •์„ ํ•ด๋‘์—ˆ๋‹ค.

728x90