๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

๐Ÿค Chapter 3: ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๊ตฌ์กฐ

๐Ÿฅ• ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•โ€‹

๐ŸŽˆ AAA ํŒจํ„ด ์‚ฌ์šฉโ€‹

AAA ํŒจํ„ด์€ ๊ฐ ํ…Œ์ŠคํŠธ๋ฅผ ์ค€๋น„, ์‹คํ–‰, ๊ฒ€์ฆ์ด๋ผ๋Š” ์„ธ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค. ๋‘ ์ˆซ์ž์˜ ํ•ฉ์„ ๊ณ„์‚ฐํ•˜๋Š” ๋ฉ”์„œ๋“œ๋งŒ ์žˆ๋Š” Calculator ํด๋ž˜์Šค๋ฅผ ์‚ดํŽด๋ณด์ž.

public class Calculator {
public double Sum(double first, double second) {
return first + second;
}
}

๋‹ค์Œ ์˜ˆ์ œ๋Š” ํด๋ž˜์Šค์˜ ๋™์ž‘์„ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ๋‹ค. ์ด ํ…Œ์ŠคํŠธ๋Š” AAA ํŒจํ„ด์„ ๋”ฐ๋ฅธ๋‹ค.

public class CalculatorTests {
[Fact]
public void Sum_of_two_numbers() {
// ์ค€๋น„
double first = 10;
double second = 20;
var calculator = new Calculator();

// ์‹คํ–‰
double result = calculator.Sum(first, second);

// ๊ฒ€์ฆ
Assert.Equal(30, result);
}
}

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

  • ์ค€๋น„ ๊ตฌ์ ˆ์—์„œ๋Š” ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ์‹œ์Šคํ…œ(SUT)๊ณผ ํ•ด๋‹น ์˜์กด์„ฑ์„ ์›ํ•˜๋Š” ์ƒํƒœ๋กœ ๋งŒ๋“ ๋‹ค.
  • ์‹คํ–‰ ๊ตฌ์ ˆ์—์„œ๋Š” SUT์—์„œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์ค€๋น„๋œ ์˜์กด์„ฑ์„ ์ „๋‹ฌํ•˜๋ฉฐ ์ถœ๋ ฅ ๊ฐ’์„ ์บก์ฒ˜ํ•œ๋‹ค.
  • ๊ฒ€์ฆ ๊ตฌ์ ˆ์—์„œ๋Š” ๊ฒฐ๊ณผ๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค. ๊ฒฐ๊ณผ๋Š” ๋ฐ˜ํ™˜ ๊ฐ’์ด๋‚˜ SUT์™€ ํ˜‘๋ ฅ์ž์˜ ์ตœ์ข… ์ƒํƒœ, SUT๊ฐ€ ํ˜‘๋ ฅ์ž์— ํ˜ธ์ถœํ•œ ๋ฉ”์„œ๋“œ ๋“ฑ์œผ๋กœ ํ‘œ์‹œ๋  ์ˆ˜ ์žˆ๋‹ค.

Given-When-Then ํŒจํ„ด
AAA์™€ ์œ ์‚ฌํ•œ Given-When-Then ํŒจํ„ด์— ๋Œ€ํ•ด ๋“ค์–ด๋ดค์„ ๊ฒƒ์ด๋‹ค. ์ด ํŒจํ„ด๋„ ํ…Œ์ŠคํŠธ๋ฅผ ์„ธ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆˆ๋‹ค.

  • Given - ์ค€๋น„ ๊ตฌ์ ˆ์— ํ•ด๋‹น
  • When - ์‹คํ–‰ ๊ตฌ์ ˆ์— ํ•ด๋‹น
  • Then - ๊ฒ€์ฆ ๊ตฌ์ ˆ์— ํ•ด๋‹น

ํ…Œ์ŠคํŠธ ๊ตฌ์„ฑ ์ธก๋ฉด์—์„œ ๋‘ ๊ฐ€์ง€ ํŒจํ„ด ์‚ฌ์ด์— ์ฐจ์ด๋Š” ์—†๋‹ค. ์œ ์ผํ•œ ์ฐจ์ด์ ์€ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ์•„๋‹Œ ์‚ฌ๋žŒ์—๊ฒŒ Given-When-Then ๊ตฌ์กฐ๊ฐ€ ๋” ์ฝ๊ธฐ ์‰ฝ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ Given-When-Then์€ ๋น„๊ธฐ์ˆ ์ž๋“ค๊ณผ ๊ณต์œ ํ•˜๋Š” ํ…Œ์ŠคํŠธ์— ๋” ์ ํ•ฉํ•˜๋‹ค.

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

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

๐ŸŽˆ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ค€๋น„, ์‹คํ–‰, ๊ฒ€์ฆ ๊ตฌ์ ˆ ํ”ผํ•˜๊ธฐโ€‹

๋•Œ๋กœ๋Š” ์ค€๋น„, ์‹คํ–‰ ๋˜๋Š” ๊ฒ€์ฆ ๊ตฌ์ ˆ์ด ์—ฌ๋Ÿฌ ๊ฐœ์žˆ๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋‚  ์ˆ˜ ์žˆ๋‹ค.

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

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์—์„œ๋Š” ์‹คํ–‰ ๊ตฌ์ ˆ์„ ์—ฌ๋Ÿฌ ๊ฐœ ๋‘๋Š” ๊ฒƒ์ด ๊ดœ์ฐฎ์„ ๋•Œ๋„ ์žˆ๋‹ค. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋Š” ๋Š๋ฆด ์ˆ˜ ์žˆ๋‹ค. ์†๋„๋ฅผ ๋†’์ด๋Š” ํ•œ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์—ฌ๋Ÿฌ ์‹คํ–‰๊ณผ ๊ฒ€์ฆ์ด ์žˆ๋Š” ๋‹จ์ผํ•œ ํ…Œ์ŠคํŠธ๋กœ ๋ฌถ๋Š” ๊ฒƒ์ด๋‹ค.

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

๐ŸŽˆ ํ…Œ์ŠคํŠธ ๋‚ด if๋ฌธ ํ”ผํ•˜๊ธฐโ€‹

์ค€๋น„, ์‹คํ–‰, ๊ฒ€์ฆ ๊ตฌ์ ˆ์ด ์—ฌ๋Ÿฌ ์ฐจ๋ก€ ๋‚˜ํƒ€๋‚˜๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•˜๊ฒŒ, if๋ฌธ์ด ์žˆ๋Š” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋‚  ์ˆ˜ ์žˆ๋‹ค. ์ด๊ฒƒ๋„ ์•ˆํ‹ฐ ํŒจํ„ด์ด๋‹ค. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋“  ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋“  ํ…Œ์ŠคํŠธ๋Š” ๋ถ„๊ธฐ๊ฐ€ ์—†๋Š” ๊ฐ„๋‹จํ•œ ์ผ๋ จ์˜ ๋‹จ๊ณ„์—ฌ์•ผ ํ•œ๋‹ค.

if๋ฌธ์€ ํ…Œ์ŠคํŠธ๊ฐ€ ํ•œ ๋ฒˆ์— ๋„ˆ๋ฌด ๋งŽ์€ ๊ฒƒ์„ ๊ฒ€์ฆํ•œ๋‹ค๋Š” ํ‘œ์‹œ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์ด๋Ÿฌํ•œ ํ…Œ์ŠคํŠธ๋Š” ๋ฐ˜๋“œ์‹œ ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŠธ๋กœ ๋‚˜๋ˆ ์•ผ ํ•œ๋‹ค. ํ…Œ์ŠคํŠธ์— ๋ถ„๊ธฐ๊ฐ€ ์žˆ์–ด์„œ ์–ป๋Š” ์ด์ ์€ ์—†๋‹ค. ๋‹จ์ง€ ์ถ”๊ฐ€ ์œ ์ง€๋น„๋งŒ ๋ถˆ์–ด๋‚œ๋‹ค. if๋ฌธ์€ ํ…Œ์ŠคํŠธ๋ฅผ ์ฝ๊ณ  ์ดํ•ดํ•˜๋Š” ๊ฒƒ์„ ๋” ์–ด๋ ต๊ฒŒ ๋งŒ๋“ ๋‹ค.

๐ŸŽˆ ๊ฐ ๊ตฌ์ ˆ์„ ์–ผ๋งˆ๋‚˜ ์ปค์•ผ ํ•˜๋Š”๊ฐ€?โ€‹

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

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

[Fact]
public void Purchase_succeeds_when_enough_inventory() {
// ์ค€๋น„
var store = new Store();
store.AddInventory(Product.Shampoo, 10);
var customer = new Customer();

// ์‹คํ–‰
bool success = customer.Purchase(store, Product.Shampoo, 5);
store.RemoveInventory(success, Product.Shampoo, 5);

// ๊ฒ€์ฆ
Assert.True(success);
Assert.Equal(5, store.GetInventory(Product.Shampoo));
}
  • ์ฒซ ๋ฒˆ์งธ ์ค„์—์„œ ๊ณ ๊ฐ์ด ์ƒ์ ์—์„œ ์ƒดํ‘ธ ๋‹ค์„ฏ ๊ฐœ๋ฅผ ์–ป์„๋ ค๊ณ  ํ•œ๋‹ค.
  • ๋‘ ๋ฒˆ์งธ ์ค„์—์„œ๋Š” ์žฌ๊ณ ๊ฐ€ ๊ฐ์†Œ๋˜๋Š”๋ฐ, Purchase() ํ˜ธ์ถœ์ด ์„ฑ๊ณต์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ์ˆ˜ํ–‰๋œ๋‹ค.

์ด๋Ÿฌํ•œ ๋ชจ์ˆœ์„ ๋ถˆ๋ณ€ ์œ„๋ฐ˜์ด๋ผ๊ณ  ํ•˜๋ฉฐ, ์ž ์žฌ์  ๋ชจ์ˆœ์œผ๋กœ๋ถ€ํ„ฐ ์ฝ”๋“œ๋ฅผ ๋ณดํ˜ธํ•˜๋Š” ํ–‰์œ„๋ฅผ ์บก์Šํ™”๋ผ๊ณ  ํ•œ๋‹ค. ๋ถˆ๋ณ€์„ ์ง€ํ‚ค๋Š” ํ•œ, ๋ถˆ๋ณ€ ์œ„๋ฐ˜์„ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ๋Š” ์ž ์žฌ์ ์ธ ํ–‰๋™์„ ์ œ๊ฑฐํ•ด์•ผ ํ•œ๋‹ค.

๐ŸŽˆ ๊ฒ€์ฆ ๊ตฌ์ ˆ์—๋Š” ๊ฒ€์ฆ๋ฌธ์ด ์–ผ๋งˆ๋‚˜ ์žˆ์–ด์•ผ ํ•˜๋Š”๊ฐ€โ€‹

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

๊ทธ๋ ‡๊ธฐ๋Š” ํ•ด๋„ ๊ฒ€์ฆ ๊ตฌ์ ˆ์ด ๋„ˆ๋ฌด ์ปค์ง€๋Š” ๊ฒƒ์€ ๊ฒฝ๊ณ„ํ•ด์•ผ ํ•œ๋‹ค. ์ œํ’ˆ ์ฝ”๋“œ์—์„œ ์ถ”์ƒํ™”๊ฐ€ ๋ˆ„๋ฝ๋์„ ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด SUT์—์„œ ๋ฐ˜ํ™˜๋œ ๊ฐ์ฒด ๋‚ด์—์„œ ๋ชจ๋“  ์†์„ฑ์„ ๊ฒ€์ฆํ•˜๋Š” ๋Œ€์‹  ๊ฐ์ฒด ํด๋ž˜์Šค ๋‚ด์— ์ ์ ˆํ•œ ๋™๋“ฑ ๋ฉค๋ฒ„๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋‹จ์ผ ๊ฒ€์ฆ๋ฌธ์œผ๋กœ ๊ฐ์ฒด๋ฅผ ๊ธฐ๋Œ€๊ฐ’๊ณผ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋‹ค.

๐ŸŽˆ ์ข…๋ฃŒ ๋‹จ๊ณ„๋ฅผ ์–ด๋–ค๊ฐ€โ€‹

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

๋Œ€๋ถ€๋ถ„์˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ์ข…๋ฃŒ ๊ตฌ์ ˆ์ด ํ•„์š” ์—†๋‹ค. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ํ”„๋กœ์„ธ์Šค ์™ธ๋ถ€์— ์ข…์†์ ์ด์ง€ ์•Š์œผ๋ฏ€๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•  ๋ถ€์ž‘์šฉ์„ ๋‚จ๊ธฐ์ง€ ์•Š๋Š”๋‹ค. ์ข…๋ฃŒ๋Š” ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์˜ ์˜์—ญ์ด๋‹ค.

๐ŸŽˆ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ์‹œ์Šคํ…œ ๊ตฌ๋ณ„ํ•˜๊ธฐโ€‹

SUT๋Š” ํ…Œ์ŠคํŠธ์—์„œ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•˜๋Š”๋ฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ํ˜ธ์ถœํ•˜๊ณ ์ž ํ•˜๋Š” ๋™์ž‘์— ๋Œ€ํ•œ ์ง„์ž…์ ‘์„ ์ œ๊ณตํ•œ๋‹ค. ๋™์ž‘์€ ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์— ๊ฑธ์ณ ์žˆ์„ ๋งŒํผ ํด ์ˆ˜๋„ ์žˆ๊ณ  ๋‹จ์ผ ๋ฉ”์„œ๋“œ๋กœ ์ž‘์„ ์ˆ˜๋„ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ง„์ž…์ ์€ ์˜ค์ง ํ•˜๋‚˜๋งŒ ์กด์žฌํ•  ์ˆ˜ ์žˆ๋‹ค.

๋”ฐ๋ผ์„œ SUT๋ฅผ ์˜์กด์„ฑ๊ณผ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค. ํŠนํžˆ SUT๊ฐ€ ๊ฝค ๋งŽ์€ ๊ฒฝ์šฐ, ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์„ ์ฐพ๋Š” ๋ฐ ์‹œ๊ฐ„์„ ๋„ˆ๋ฌด ๋งŽ์ด ๋“ค์ผ ํ•„์š”๊ฐ€ ์—†๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ํ…Œ์ŠคํŠธ ๋‚ด SUT ์ด๋ฆ„์„ sut๋กœ ํ•˜๋ผ.

public class CalculatorTests {
[Fact]
public void Sum_of_two_numbers() {
// ์ค€๋น„
double first = 10;
double second = 20;
var sut = new Calculator(); // Calculator๋ฅผ ์ด์ œ sut๋ผ๊ณ  ํ•œ๋‹ค.

// ์‹คํ–‰
double result = sut.Sum(first, second);

// ๊ฒ€์ฆ
Assert.Equal(30, result);
}
}

๐ŸŽˆ ์ค€๋น„, ์‹คํ–‰, ๊ฒ€์ฆ ์ฃผ์„ ์ œ๊ฑฐํ•˜๊ธฐโ€‹

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

public class CalculatorTests {
[Fact]
public void Sum_of_two_numbers() {
double first = 10;
double second = 20;
var sut = new Calculator();

double result = sut.Sum(first, second);

Assert.Equal(30, result);
}
}

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

  • AAA ํŒจํ„ด์„ ๋”ฐ๋ฅด๊ณ  ์ค€๋น„ ๋ฐ ๊ฒ€์ฆ ๊ตฌ์ ˆ์— ๋นˆ ์ค„์„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ํ…Œ์ŠคํŠธ๋ผ๋ฉด ๊ตฌ์ ˆ ์ฃผ์„๋“ค์„ ์ œ๊ฑฐํ•˜๋ผ.
  • ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๊ตฌ์ ˆ ์ฃผ์„์„ ์œ ์ง€ํ•˜๋ผ.

๐Ÿฅ• xUnit ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ ์‚ดํŽด๋ณด๊ธฐโ€‹

  • ํ”„๋ ˆ์ž„์›Œํฌ ์†Œ๊ฐœ๋ผ ์ƒ๋žต. (P.89 ~ P.90 ์ฐธ๊ณ )

๐Ÿฅ• ํ…Œ์ŠคํŠธ ๊ฐ„ ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ฒ˜ ์žฌ์‚ฌ์šฉโ€‹

์ค€๋น„ ๊ตฌ์ ˆ์—์„œ ์ฝ”๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ํ…Œ์ŠคํŠธ๋ฅผ ์ค„์ด๋ฉด์„œ ๋‹จ์ˆœํ™”ํ•˜๊ธฐ ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋‹ค.

ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ฒ˜
ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ฒ˜๋ผ๋Š” ๋‹จ์–ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‘ ๊ฐ€์ง€ ๊ณตํ†ต๋œ ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค.

  1. ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ฒ˜๋Š” ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๋Œ€์ƒ ๊ฐ์ฒด๋‹ค. ์ด ๊ฐ์ฒด๋Š” ์ •๊ทœ ์˜์กด์„ฑ, ์ฆ‰ SUT๋กœ ์ „๋‹ฌ๋˜๋Š” ์ธ์ˆ˜๋‹ค. ์ด๋Ÿฌํ•œ ๊ฐ์ฒด๋Š” ๊ฐ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์ „์— ์•Œ๋ ค์ง„ ๊ณ ์ • ์ƒํƒœ๋กœ ์œ ์ง€ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ํ”ฝ์Šค์ฒ˜๋ผ๋Š” ๋‹จ์–ด๊ฐ€ ๋‚˜์™”๋‹ค.
  2. ๋‹ค๋ฅธ ์ •์˜๋Š” NUnit ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ๋น„๋กฏ๋œ๋‹ค. NUnit์—์„œ [TestFixture]๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ํฌํ•จ๋œ ํด๋ž˜์Šค๋ฅผ ํ‘œ์‹œํ•˜๋Š” ํŠน์„ฑ์ด๋‹ค.

์ด ์ฑ…์—์„œ๋Š” ์ฒซ ๋ฒˆ์งธ ์ •์˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

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

public class CustomerTests {
private readonly Store _store; // ๊ณตํ†ต ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ฒ˜
private readonly Customer _sut;

// ํด๋ž˜์Šค ๋‚ด ๊ฐ ํ…Œ์ŠคํŠธ ์ด์ „์— ํ˜ธ์ถœ
public CustomerTests() {
_store = new Store();
_store.AddInventory(Product.Shampoo, 10);
_sut = new Customer();
}

[Fact]
public void Purchase_succeeds_when_enough_inventory() {
bool success = _sut.Purchase(_store, Product.Shampoo, 5);
Assert.True(success);
Assert.Equal(5, _store.GetInventory(Product,Shampoo));
}

[Fact]
public void Purchase_fails_when_not_enough_inventory() {
bool success = _sut.Purchase(_store, Product.Shampoo, 15);
Assert.False(success);
Assert.Equal(10, _store.GetInventory(Product.Shampoo));
}
}

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

  • ํ…Œ์ŠคํŠธ ๊ฐ„ ๊ฒฐํ•ฉ๋„๊ฐ€ ๋†’์•„์ง„๋‹ค.
  • ํ…Œ์ŠคํŠธ ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง„๋‹ค.

๐ŸŽˆ ํ…Œ์ŠคํŠธ ๊ฐ„์˜ ๋†’์€ ๊ฒฐํ•ฉ๋„๋Š” ์•ˆํ‹ฐ ํŒจํ„ด์ด๋‹คโ€‹

์œ„ ์˜ˆ์ œ์—์„œ ๋ชจ๋“  ํ…Œ์ŠคํŠธ๊ฐ€ ์„œ๋กœ ๊ฒฐํ•ฉ๋ผ ์žˆ๋‹ค. ์ฆ‰, ํ…Œ์ŠคํŠธ์˜ ์ค€๋น„ ๋กœ์ง์„ ์ˆ˜์ •ํ•˜๋ฉด ํด๋ž˜์Šค์˜ ๋ชจ๋“  ํ…Œ์ŠคํŠธ์— ์˜ํ–ฅ์„ ๋ฏธ์นœ๋‹ค. ์—๋ฅผ ๋“ค์–ด,

_store.AddInventory(Product.Shampoo, 10);

์œ„ ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜๋ฉด

_store.AddInventory(Product.Shampoo, 15);

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

๐ŸŽˆ ํ…Œ์ŠคํŠธ ๊ฐ€๋…์„ฑ์„ ๋–จ์–ด๋œจ๋ฆฌ๋Š” ์ƒ์„ฑ์ž ์‚ฌ์šฉโ€‹

์ค€๋น„ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑ์ž๋กœ ์ถ”์ถœํ•  ๋•Œ์˜ ๋˜ ๋‹ค๋ฅธ ๋‹จ์ ์€ ํ…Œ์ŠคํŠธ ๊ฐ€๋…์„ฑ์„ ๋–จ์–ด๋œจ๋ฆฌ๋Š” ๊ฒƒ์ด๋‹ค. ํ…Œ์ŠคํŠธ๋งŒ ๋ณด๊ณ  ๋” ์ด์ƒ ์ „์ฒด ๊ทธ๋ฆผ์„ ๋ณผ ์ˆ˜ ์—†๋‹ค. ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ์ดํ•ดํ•˜๋ ค๋ฉด ํด๋ž˜์Šค์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„๋„ ๋ด์•ผ ํ•œ๋‹ค.

์ค€๋น„ ๋กœ์ง์ด ๋ณ„๋กœ ์—†๋”๋ผ๋„ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๋กœ ๋ฐ”๋กœ ์˜ฎ๊ธฐ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

๐ŸŽˆ ๋” ๋‚˜์€ ํ…Œ์ŠคํŠธ ํ”ฝ์Šค์ฒ˜ ์žฌ์‚ฌ์šฉ๋ฒ•โ€‹

๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ ์˜ˆ์ œ์™€ ๊ฐ™์ด ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค์— ๋น„๊ณต๊ฐœ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ๋‘๋Š” ๊ฒƒ์ด๋‹ค.

public class CustomerTests {
[Fact]
public void Purchase_succeeds_when_enough_inventory() {
Store store = CreateStoreWithInventory(Product.Shampoo, 10);
Customer sut = CreateCustomer();
bool success = sut.Purchase(store, Product.Shampoo, 5);
Assert.True(success);
Assert.Equal(5, store.GetInventory(Product.Shampoo));
}

[Fact]
public void Purchase_fails_when_enough_inventory() {
Store store = CreateStoreWithInventory(Product.Shampoo, 10);
Customer sut = CreateCustomer();
bool success = sut.Purchase(store, Product.Shampoo, 15);
Assert.False(success);
Assert.Equal(10, store.GetInventory(Product.Shampoo));
}

private Store CreateStoreWithInventory(Product product, int quantity) {
Store store = new Store();
store.AddInventory(product, quantity);
return store;
}

private state Customer CreateCustomer() {
return new Customer();
}
}

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

Store store = CreateStoreWithInventory(Product.Shampoo, 10);

์ƒ์„ฑ๋œ ์ƒ์ ์˜ ํŠน์„ฑ์„ ์ดํ•ดํ•˜๋ ค๊ณ  ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€๋ฅผ ์•Œ์•„๋ณผ ํ•„์š”๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋…์„ฑ์ด ์ข‹๋‹ค. ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ์—์„œ๋„ ์ด ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์žฌ์‚ฌ์šฉ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

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

public class CustomerTests : IntegrationTests {
[Fact]
public void Purchase_succeeds_when_enough_inventory() {
/* ์—ฌ๊ธฐ์„œ _database ์‚ฌ์šฉ */
}
}

public abstract class IntegrationTests : IDisposable {
protected readonly Database _database;

protected IntegrationTests() {
_database = new Database();
}

public void Dispose() {
_database.Dispose();
}
}

CustomerTests๊ฐ€ ์ƒ์„ฑ์ž ์—†์ด ์ž‘์„ฑ๋๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. IntegrationTests ๊ธฐ์ดˆ ํด๋ž˜์Šค ์ƒ์†์„ ํ†ตํ•ด _database ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผํ•œ๋‹ค.

๐Ÿฅ• ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๋ช…๋ช…๋ฒ•โ€‹

ํ…Œ์ŠคํŠธ์— ํ‘œํ˜„๋ ฅ์ด ์žˆ๋Š” ์ด๋ฆ„์„ ๋ถ™์ด๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค. ์˜ฌ๋ฐ”๋ฅธ ๋ช…์นญ์€ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฒ€์ฆํ•˜๋Š” ๋‚ด์šฉ๊ณผ ๊ธฐ๋ณธ ์‹œ์Šคํ…œ์˜ ๋™์ž‘์„ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋œ๋‹ค.

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

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

public void Sum_of_two_numbers() {}
public void Sum_TwoNumbers_ReturnsSum() {}

์‰ฌ์šด ์˜์–ด๋กœ ์ž‘์„ฑํ•œ ์ฒซ ๋ฒˆ์งธ ์ด๋ฆ„์ด ์ฝ๊ธฐ์— ํ›จ์”ฌ ๊ฐ„๊ฒฐํ•˜๋‹ค. ์ด๋Š” ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ๋™์ž‘์— ๋Œ€ํ•œ ํ˜„์‹ค์ ์ธ ์„ค๋ช…์ด๋‹ค.

๐ŸŽˆ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๋ช…๋ช… ์ง€์นจโ€‹

ํ‘œํ˜„๋ ฅ ์žˆ๊ณ  ์ฝ๊ธฐ ์‰ฌ์šด ํ…Œ์ŠคํŠธ ์ด๋ฆ„์„ ์ง€์œผ๋ ค๋ฉด ๋‹ค์Œ ์ง€์นจ์„ ๋”ฐ๋ฅด์ž.

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

๋ณดํ†ต ํด๋ž˜์Šค ์ด๋ฆ„์€ ๊ทธ๋ฆฌ ๊ธธ์ง€ ์•Š์•„์„œ ๋ฐ‘์ค„ ํ‘œ์‹œ๊ฐ€ ์—†์–ด๋„ ์ž˜ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. ๋˜ ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ์ด๋ฆ„์„ ์ง€์ •ํ•  ๋•Œ [ํด๋ž˜์Šค๋ช…]Tests ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ํ…Œ์ŠคํŠธ๊ฐ€ ํ•ด๋‹น ํด๋ž˜์Šค๋งŒ ๊ฒ€์ฆํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ œํ•œํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์—์„œ ๋‹จ์œ„๋Š” ๋™์ž‘์˜ ๋‹จ์œ„์ง€, ํด๋ž˜์Šค์˜ ๋‹จ์œ„๊ฐ€ ์•„๋‹Œ ๊ฒƒ์„ ๋ช…์‹ฌํ•˜์ž.

๐ŸŽˆ ์˜ˆ์ œ: ์ง€์นจ์— ๋”ฐ๋ฅธ ํ…Œ์ŠคํŠธ ์ด๋ฆ„ ๋ณ€๊ฒฝโ€‹

  • ์˜ˆ์ œ ๋‚ด์šฉ P.99 ~ P.101 ์ฐธ๊ณ 
public void IsDeliveryValid_InvalidDate_ReturnsFalse() {} // ๋ณ€๊ฒฝ ์ „
public void Delivery_with_a_past_date_isValid() {} // ๋ณ€๊ฒฝ ํ›„

์ƒˆ๋กœ์šด ๋ฒ„์ „์—์„œ ๋‘ ๊ฐ€์ง€๊ฐ€ ๋ˆˆ์— ๋ˆ๋‹ค.

  • ์ด์ œ ์ด๋ฆ„์ด ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ์•„๋‹Œ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ๋‚ฉ๋“๋˜๊ณ , ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋„ ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.
  • SUT์˜ ๋ฉ”์„œ๋“œ ์ด๋ฆ„(IsDeliveryValid)์€ ๋” ์ด์ƒ ํ…Œ์ŠคํŠธ๋ช…์— ํฌํ•จ๋˜์ง€ ์•Š๋Š”๋‹ค.

๐Ÿฅ• ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”๋œ ํ…Œ์ŠคํŠธ ๋ฆฌํŒฉํ„ฐ๋งํ•˜๊ธฐโ€‹

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

๊ฐ€์žฅ ๋น ๋ฅธ ๋ฐฐ์†ก์ผ์ด ์˜ค๋Š˜๋กœ๋ถ€ํ„ฐ ์ดํ‹€ ํ›„๊ฐ€ ๋˜๋„๋ก ์ž‘๋™ํ•˜๋Š” ๋ฐฐ์†ก ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž. ๋ถ„๋ช…ํžˆ ํ…Œ์ŠคํŠธ ํ•˜๋‚˜๋กœ๋Š” ์ถฉ๋ถ„ํ•˜์ง€ ์•Š๋‹ค. ์ž‘์„ฑํ–ˆ๋˜ ํ…Œ์ŠคํŠธ๋ฅผ Delivery_with_a_past_date_isValid๋กœ ํ•˜์ž. ๋‹ค์Œ ๋ฉ”์„œ๋“œ ์„ธ ๊ฐœ๋ฅผ ์ถ”๊ฐ€ํ•ด๋ณด์ž.

public void Delivery_for_today_is_invalid() {}
public void Delivery_for_tomorrow_is_invalid() {}
public void The_soonest_delivery_date_is_two_days_from_now() {}

ํ•˜์ง€๋งŒ ์ด๋กœ ์ธํ•ด ๋„ค ๊ฐ€์ง€์˜ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ๊ฐ€ ๋‚˜์™”์ง€๋งŒ, ์œ ์ผํ•œ ์ฐจ์ด์ ์€ ๋ฐฐ์†ก ๋‚ ์งœ๋ฟ ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ์–‘์„ ์ค„์ด๊ณ ์ž ์ด๋Ÿฌํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋‚˜๋กœ ๋ฌถ๋Š” ๊ฒƒ์ด๋‹ค.

// ๋ช‡ ๊ฐ€์ง€ ์‚ฌ์‹ค์„ ํฌ๊ด„ํ•˜๋Š” ํ…Œ์ŠคํŠธ
public class DeliveryServiceTests {
// [InlineData] ํŠน์„ฑ์€ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ์— ์ž…๋ ฅ ๊ฐ’ ์ง‘ํ•ฉ์„ ๋ณด๋‚ธ๋‹ค. ๊ฐ ๋ผ์ธ์€ ๋™์ž‘์— ๋Œ€ํ•ด ๋ณ„๊ฐœ์˜ ์‚ฌ์‹ค์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
[InlineData(-1, false)]
[InlineData(0, false)]
[InlineData(1, false)]
[InlineData(2, true)]
[Theory]
public void Can_detect_an_invalid_delivery_date(int daysFromNow, bool expected) {
DeliveryService sut = new DeliveryService();
DateTime deliveryDate = DateTime.Now.AddDays(daysFromNow);
Delivery delivery = new Delivery {
Date = deliveryDate
};

bool isValid = sut.IsDeliveryValid(delivery);

Assert.Equal(expected, isValid);
}
}

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

// ๊ธ์ •์ ์ธ ์‹œ๋‚˜๋ฆฌ์˜ค์™€ ๋ถ€์ •์ ์ธ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ํ…Œ์ŠคํŠธ
public class DeliveryServiceTests {
[InlineData(-1)]
[InlineData(0)]
[InlineData(1)]
[Theory]
public void Detects_an_invalid_delivery_date(int daysFromNow) {
// ...
}

[Fact]
public void The_soonest_delivery_date_is_two_days_from_now() {
// ...
}
}

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

๐ŸŽˆ ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”๋œ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑโ€‹

๋งค๊ฐœ๋ณ€์ˆ˜ํ™”๋œ ํ…Œ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์ด ์žˆ๋‹ค.

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

// ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”๋œ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ
[Theory]
[MemberData(nameof(Data))]
public void Can_detect_an_invalid_delivery_date(DateTime deliveryDate, bool expected) {
// ...
}

public static List<object[]> Data() {
return new List<object[]> {
new object[] { DateTime.Now.AddDays(-1), false },
new object[] { DateTime.Now, false },
new object[] { DateTime.Now.AddDays(1), false },
new object[] { DateTime.Now.AddDays(2), true },
}
}

๐Ÿฅ• ๊ฒ€์ฆ๋ฌธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ ํ…Œ์ŠคํŠธ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒโ€‹

๊ฒ€์ฆ๋ฌธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฃผ์š” ์ด์ ์€ ๊ฒ€์ฆ๋ฌธ์„ ์žฌ๊ตฌ์„ฑํ•ด ๊ฐ€๋…์„ฑ์„ ๋†’์ด๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ๋‹ค์Œ์€ ์ด์ „ ํ…Œ์ŠคํŠธ ์ค‘ ํ•˜๋‚˜๋‹ค.

[Fact]
public void Sum_of_two_numbers() {
var sut = new Calculator();
double result = sut.Sum(10, 20);
Assert.Equal(30, result);
}

์ด์ œ ๋‹ค์Œ ์ฝ”๋“œ์™€ ๊ฐ™์ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ฒ€์ฆ๋ฌธ์„ ์ˆ˜์ •ํ•ด์„œ ๋น„๊ตํ•ด๋ณด์ž.

[Fact]
public void Sum_of_two_numbers() {
var sut = new Calculator();
double result = sut.Sum(10, 20);
result.Should().Be(30);
}

๋‘ ๋ฒˆ์งธ ํ…Œ์ŠคํŠธ์˜ ๊ฒ€์ฆ๋ฌธ์€ ์‰ฌ์šด ์˜์–ด๋กœ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” ์ •ํ™•ํžˆ ์ฝ”๋“œ๋ฅผ ์ฝ๊ณ  ์‹ถ์€ ๋ฐฉ์‹์ด๋‹ค.
result.Should().Be(30)์€ ์ด์•ผ๊ธฐ ํŒจํ„ด์„ ๋”ฐ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— Assert.Equal(30, result)๋ณด๋‹ค ๋” ์ž˜ ์ฝํžŒ๋‹ค.