๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Spring/Test Driven Development

01. TDD๋ž€?

๐Ÿ’ก ๋ณธ ๊ฒŒ์‹œ๊ธ€์€ ์ตœ๋ฒ”๊ท  ์ €์ž๋‹˜์˜ "ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ ์‹œ์ž‘ํ•˜๊ธฐ"์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•˜๊ณ , ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.


Chapter 02. TDD ์‹œ์ž‘

1) TDD๋ž€?

  • TDD(Test-Driven Development)๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๋Š” ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ•๋ก ์ž…๋‹ˆ๋‹ค. ๊ธฐ๋Šฅ์„ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๊ณ , ์ด ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์ตœ์†Œํ•œ์˜ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

2) ๊ฐ„๋‹จํ•œ ๋ง์…ˆ ๊ธฐ๋Šฅ์„ TDD๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ

1. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

  • Calculator ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์•˜์œผ๋‹ˆ ๋‹น์—ฐํžˆ ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • public class CalculatorTest { @Test void plus() { int result = Calculator.plus(1, 2); assertEquals(3, result); } }

2. ๊ตฌํ˜„ํ•  ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ๊ณ ๋ฏผํ•˜๊ธฐ

  • ๋ง์…ˆ ๋ฉ”์„œ๋“œ ์ด๋ฆ„์€ plus๊ฐ€ ์ข‹์„๊นŒ? ์•„๋‹ˆ๋ฉด sum์ด ์ข‹์„๊นŒ?
  • ๋ง์…ˆ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋ช‡ ๊ฐœ์—ฌ์•ผ ํ• ๊นŒ? ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ํƒ€์ž…์€? ๋ฐ˜ํ™˜ํ•  ๊ฐ’์€?
  • ๋ฉ”์„œ๋“œ๋ฅผ ์ •์  ๋ฉ”์„œ๋“œ๋กœ ๊ตฌํ˜„ํ• ๊นŒ ์ธ์Šคํ„ด์Šค ๋ฉ”์„œ๋“œ๋กœ ๊ตฌํ˜„ํ• ๊นŒ?
  • ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•  ํด๋ž˜์Šค ์ด๋ฆ„์€ ๋ญ๊ฐ€ ์ข‹์„๊นŒ?

3. Calculator ํด๋ž˜์Šค ๊ตฌํ˜„

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

public class Calculator {
    public static int plus(int a1, int a2) {
        return 0;
    }
}

(2) ์‹คํŒจ๋œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ฒฐ๊ณผ๊ฐ’์„ 3์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ์„ฑ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ๋ง์…ˆ ๊ฒ€์ฆ ์ฝ”๋“œ๋ฅผ ํ•˜๋‚˜ ๋” ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

public class Calculator {
    public static int plus(int a1, int a2) {
        return 3;
    }
}

(3) ํ•ด๋‹น ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ ์‹คํŒจ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@Test
void plus() {
    int result = Calculator.plus(1, 2);
    assertEquals(3, result);
    assertEquals(5, Calculator.plus(4, 1));
}

(4) ๋‘ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋งŒ์กฑํ•˜๋Š” ๋ง์…ˆ ๋กœ์ง์œผ๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋ ‡๊ฒŒ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๋‹ค ๋ณด๋ฉด ๊ฒฐ๊ตญ ์•„๋ž˜์™€ ๊ฐ™์€ ๊ตฌํ˜„ ์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

public class Calculator {
    public static int plus(int a1, int a2) {
        if (a1 == 4 && a2 == 1) return 5;
        else return 3;
    }
}

(5) ๋ง์…ˆ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ TDD๋กœ ๊ตฌํ˜„ํ•ด ๋ดค์Šต๋‹ˆ๋‹ค.

public class Calculator {
    public static int plus(int a1, int a2) {
        return a1 + a2;
    }
}

3) TDD ์˜ˆ: ์•”ํ˜ธ ๊ฒ€์‚ฌ๊ธฐ

  • ์•”ํ˜ธ ๊ฒ€์‚ฌ๊ธฐ๋Š” ๋ฌธ์ž์—ด์„ ๊ฒ€์‚ฌํ•ด์„œ ๊ทœ์น™์„ ์ค€์ˆ˜ํ•˜๋Š”์ง€์— ๋”ฐ๋ผ ์•”ํ˜ธ๋ฅผ '์•ฝํ•จ', '๋ณดํ†ต', '๊ฐ•ํ•จ'์œผ๋กœ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

1. ๊ฒ€์‚ฌํ•  ๊ทœ์น™

  1. ๊ธธ์ด๊ฐ€ 8๊ธ€์ž ์ด์ƒ
  2. 0๋ถ€ํ„ฐ 9 ์‚ฌ์ด์˜ ์ˆซ์ž๋ฅผ ํฌํ•จ
  3. ๋Œ€๋ฌธ์ž ํฌํ•จ

2. ๊ทœ์น™์— ๋”ฐ๋ฅธ ๋ถ„๋ฅ˜

  • ์„ธ ๊ทœ์น™์„ ๋ชจ๋‘ ์ถฉ์กฑํ•˜๋ฉด ์•”ํ˜ธ๋Š” ๊ฐ•ํ•จ์ž…๋‹ˆ๋‹ค.
  • ๋‘ ๊ฐœ์˜ ๊ทœ์น™์„ ์ถฉ์กฑํ•˜๋ฉด ์•”ํ˜ธ๋Š” ๋ณดํ†ต์ž…๋‹ˆ๋‹ค.
  • ํ•œ ๊ฐœ ์ดํ•˜์˜ ๊ทœ์น™์„ ์ถฉ์กฑํ•˜๋ฉด ์•”ํ˜ธ๋Š” ์•ฝํ•จ์ž…๋‹ˆ๋‹ค.

3. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

public class PasswordStrengthMeterTest {
    @Test
    void name() { }
}

(1) ์ฒซ ๋ฒˆ์งธ ํ…Œ์ŠคํŠธ: ๋ชจ๋“  ๊ทœ์น™์„ ์ถฉ์กฑํ•˜๋Š” ๊ฒฝ์šฐ

@Test
void meetsAllCriteria_Then_Strong() {
    private PasswordStrengthMeter meter = new PasswordStrengthMeter();
    PasswordStrength result = meter.meter("ab12!@AB");
    assertEquals(PasswordStrength.STRONG, result);
}

(2) PasswordStrengthMeter ํƒ€์ž…๊ณผ PasswordStrength ํƒ€์ž…์ด ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํƒ€์ž…์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

public enum PasswordStrength {
    STRONG
}

public class PasswordStrengthMeter {
    public PasswordStrength meter(String s) { return null; }
}

4. ๋ชจ๋“  ๊ทœ์น™์„ ์ถฉ์กฑํ•˜๋Š” ํ…Œ์ŠคํŠธ ํ†ต๊ณผ

public class PasswordStrengthMeter {
    public PasswordStrength meter(String s) { return PasswordStrength.STRONG; }
}

5. ์กฐ๊ฑด๋ณ„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ ๋ฐ ๊ตฌํ˜„

  • ๊ฐ ์กฐ๊ฑด๋ณ„๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , ํ•ด๋‹น ์กฐ๊ฑด์„ ํ†ต๊ณผํ•˜๋„๋ก ๊ตฌํ˜„ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ ์ง„์ ์œผ๋กœ ๊ธฐ๋Šฅ์„ ์™„์„ฑํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ)

  • ๊ธธ์ด๋งŒ 8๊ธ€์ž ๋ฏธ๋งŒ์ด๊ณ  ๋‚˜๋จธ์ง€ ์กฐ๊ฑด์„ ์ถฉ์กฑํ•˜๋Š” ๊ฒฝ์šฐ
@Test
void meetsOtherCriteria_except_for_length_Then_Normal() {
    private PasswordStrengthMeter meter = new PasswordStrengthMeter();
    PasswordStrength result = meter.meter("ab!@ABqwer");
    assertStrength(result, PasswordStrength.NORMAL);
}
  • ๊ตฌํ˜„ ์ฝ”๋“œ
public class PasswordStrengthMeter {
    public PasswordStrength meter(String s) { 
        if (s.length() < 8) {
            return PasswordStrength.NORMAL;
        }
        return PasswordStrength.STRONG; 
    }
}

6. ์ฝ”๋“œ ์ •๋ฆฌ ๋ฐ ๋ฆฌํŒฉํ† ๋ง

  • ํ…Œ์ŠคํŠธ๊ฐ€ ๋ชจ๋‘ ํ†ต๊ณผํ•œ ํ›„์—๋Š” ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•˜์—ฌ ๊ฐ€๋…์„ฑ์„ ๋†’์ด๊ณ  ์ค‘๋ณต์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

4) TDD ๋‚ด์šฉ ์ •๋ฆฌ

1. TDD ํ๋ฆ„

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

2. ๋ ˆ๋“œ - ๊ทธ๋ฆฐ - ๋ฆฌํŒฉํ„ฐ

  • ๋ ˆ๋“œ(Red): ์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ๊ทธ๋ฆฐ(Green): ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผ์‹œํ‚ค๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฆฌํŒฉํ„ฐ(Refactor): ์ฝ”๋“œ๋ฅผ ์ •๋ฆฌํ•˜๊ณ  ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.

3. ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐœ๋ฐœ์„ ์ฃผ๋„

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

4. ์ง€์†์ ์ธ ์ฝ”๋“œ ์ •๋ฆฌ

  • ๊ตฌํ˜„์„ ์™„๋ฃŒํ•œ ํ›„์—๋Š” ๋ฆฌํŒฉํ† ๋ง์„ ์ง„ํ–‰ํ•˜์—ฌ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ๋ฆฌํŒฉํ† ๋ง์„ ๋ณด๋‹ค ๊ณผ๊ฐํ•˜๊ฒŒ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

5. ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ

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

5) ๊ฒฐ๋ก 

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