TDD

TDD๋ž€?

Test-Driven-Development(ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ)๋กœ ๊ฐœ๋ฐœ(์ฝ”๋“œ ์ž‘์„ฑ)์ „์— ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜์—ฌ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐœ๋ฐœ์„ ์ด๋Œ์–ด ๋‚˜๊ฐ€๋„๋ก ํ•˜๋Š” ๋ฐฉ์‹

"ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๋Š”, ์ฆ‰ ๊ตฌํ˜„๋ณด๋‹ค ์ธํ„ฐํŽ˜์ด์Šค์™€ ์ŠคํŽ™์„ ๋จผ์ € ์ •์˜ํ•จ์œผ๋กœ์จ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค."

TDD ์ˆœ์„œ

Red(์‹คํŒจ) โ†’ Green(์„ฑ๊ณต) โ†’ Refactoring(๋ฆฌํŒฉํ† ๋ง) ๊ณผ์ •์„ ๋ฐ˜๋ณตํ•˜๋Š” ์‚ฌ์ดํด์„ ๊ฐ–๋Š”๋‹ค.

  1. ๊ธฐ๋Šฅ ์š”๊ตฌ์‚ฌํ•ญ์„ ๋ฐ”ํƒ•์œผ๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

  2. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์‹คํ–‰ (Fail) โ†’ ์•„์ง ๊ตฌํ˜„์ฒด๊ฐ€ ์—†์œผ๋ฏ€๋กœ ๋‹น์—ฐํžˆ ์‹คํŒจ

  3. ์ž‘์„ฑ๋œ ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฐ„๋‹จํ•œ ์ƒ˜ํ”Œ ์ฝ”๋“œ ์ž‘์„ฑ

  4. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์‹คํ–‰ (Success)

  5. 3, 4๋ฒˆ ๋‹จ๊ณ„๋ฅผ ๋ฐ˜๋ณตํ•˜๋ฉฐ ์ฝ”๋“œ๋ฅผ ๊ฐœ๋ฐœ

  6. ์ž‘์„ฑํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ฆฌํŒฉํ† ๋ง

์ž‘์€ ๋‹จ๊ณ„๋ฅผ ์ฐพ๊ณ , ์ฝ”๋“œ์—์„œ ํ”ผ๋“œ๋ฐฑ์„ ์–ป๋Š”๊ฒŒ ์ค‘์š”ํ•˜๋‹ค. 3๋ฒˆ์ด ์–ด๋ ต๋‹ค๋ฉด 1๋ฒˆ์œผ๋กœ ๋Œ์•„๊ฐ€์„œ ๋” ์ž‘๊ณ  ์‰ฌ์šด ๋ฌธ์ œ๋ฅผ ์ •์˜ํ•˜๊ณ , 6๋ฒˆ์„ ์œ„ํ•ด ์˜๋„๋ฅผ ๋“œ๋Ÿฌ๋‚ด๊ณ  ์ค‘๋ณต์„ ์ฐพ์•„ ์ œ๊ฑฐํ•˜๋Š” ์—ฐ์Šต์„ ํ•ด์•ผ ํ•œ๋‹ค. ์ด ๋‘˜์„ ์ตํžˆ์ง€ ์•Š์œผ๋ฉด TDD๋ฅผ ํ•˜๋Š”๊ฒŒ ๊ฑฐ์˜ ๋ถˆ๊ฐ€๋Šฅ,

TDD ์›์น™

  • ์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๋ผ โ†’ ์ฆ‰, ๋ฏธ๋ฆฌ ๊ตฌํ˜„ํ•˜์ง€๋ง๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ๋จผ์ € ์ž‘์„ฑ(์š”๊ตฌ์‚ฌํ•ญ ๋ถ„์„)ํ•œ ํ›„์— ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

  • ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๋งŒํผ์˜ ์ฝ”๋“œ๋งŒ ์ž‘์„ฑํ•˜๋ผ โ†’ ๋ฏธ๋ž˜์— ํ•„์š”ํ• ๊ฒƒ ๊ฐ™์€ ๊ตฌํ˜„์„ ์ง€๋ ˆ์ง์ž‘ํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์˜ค๋ฒ„์—”์ง€๋‹ˆ์–ด๋ง์„ ๋ฐฉ์ง€ํ•œ๋‹ค.(YAGNI ์›์น™)

  • ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฌธ์ œ๋ถ€ํ„ฐ ๊ตฌํ˜„ํ•˜๋ผ โ†’ ๊ฐ„๋‹จํ•œ ๊ธฐ๋Šฅ๋ถ€ํ„ฐ ์ฐจ๊ทผ์ฐจ๊ทผ ๊ตฌํ˜„ํ•˜๋ฉฐ ์ „์ฒด์ ์ธ ๊ธฐ๋Šฅ์„ ์™„์„ฑํ•ด ๋‚˜๊ฐ€๋Š” ๊ฒƒ์ด ์ค‘์š”

  • ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€ 100%๋ฅผ ๋ชฉํ‘œ๋กœ ํ•˜์ง€ ๋ง๋ผ โ†’ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์ฝ”๋“œ๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ๊ฒƒ์ด ๋ชฉ์ ์ด ๋˜์–ด์•ผ ํ•œ๋‹ค

TDD ์žฅ์ 

  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์š”๊ตฌ์‚ฌํ•ญ์„ ๋จผ์ € ๋ถ„์„ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ชจ๋“  ์š”๊ตฌ์‚ฌํ•ญ(๋ชฉํ‘œ)์— ๋Œ€ํ•ด ์ดํ•ดํ•˜๊ณ  ์ ๊ฒ€ํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ–๊ฒŒ ๋œ๋‹ค.

  • ์˜ˆ์™ธ ์ผ€์ด์Šค๋ฅผ ๋ฏธ๋ฆฌ ๊ณ ๋ฏผํ•˜๊ณ  ์ •๋ฆฌํ•˜๋Š” ๊ณผ์ •์„ ํ†ตํ•ด ๋ฒ„๊ทธ๊ฐ€ ์ƒ๊ธฐ๋Š” ๊ฒƒ์„ ์‚ฌ์ „์— ๋ฐฉ์ง€ ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ํ…Œ์ŠคํŠธ๊ฐ€ ์‰ฌ์šด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•จ์œผ๋กœ์จ ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

    • ์ธํ„ฐํŽ˜์ด์Šค ์œ„์ฃผ์˜ ์ฝ”๋“œ ์ž‘์„ฑ

    • ์‹œ์Šคํ…œ ์ „๋ฐ˜์ ์ธ ์„ค๊ณ„ ํ–ฅ์ƒ

    • ๊ฐœ๋ฐœ ์ง‘์ค‘๋ ฅ ํ–ฅ์ƒ

  • ์„ค๊ณ„์— ๋Œ€ํ•œ ํ”ผ๋“œ๋ฐฑ์ด ๋น ๋ฅด๋‹ค.

  • ๊ฐœ๋ฐœ์— ์ž์‹ ๊ฐ์ด ์ƒ๊ธด๋‹ค

TDD ๋‹จ์ 

  • TDD๋Š” ์ƒˆ๋กœ์šด ๊ฐœ๋… ๋ฐ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋ฐฐ์›Œ์•ผํ•˜๋ฏ€๋กœ ํ•™์Šต ๊ณก์„ ์ด ๋†’์„ ์ˆ˜ ์žˆ๋‹ค.

  • ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑ ๋ฐ ์‹คํ–‰ํ•˜๋Š” ์‹œ๊ฐ„, ์œ ์ง€๋ณด์ˆ˜ ์‹œ๊ฐ„ ๋“ฑ์˜ ๋ถ€๊ฐ€์ ์ธ ์‹œ๊ฐ„์ด ํ•„์š”ํ•˜๋ฏ€๋กœ ์ „์ฒด ๊ฐœ๋ฐœ ์‹œ๊ฐ„์ด ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜๋ฏธ์žˆ๋Š” ํ…Œ์ŠคํŠธ ์ž‘์„ฑํ•˜๊ธฐ

ํ…Œ์ŠคํŠธ ํ•ด์•ผํ•  ๊ฒƒ

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

  • ์‚ฌ์šฉ์ž ์š”๊ตฌ์‚ฌํ•ญ์ด ๋ชจ๋‘ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šคํ™” ๋˜์—ˆ๋Š”์ง€

  • ์ •์ƒ์ ์ธ ์ž…๋ ฅ๊ฐ’์ด ๋“ค์–ด์˜จ ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ์‘๋‹ตํ•˜๋Š”์ง€

  • ์ •์ƒ์ ์ด์ง€ ์•Š์€ ์ž…๋ ฅ์ด ๋“ค์–ด์˜จ ๊ฒฝ์šฐ(์—ฃ์ง€์ผ€์ด์Šค) ์–ด๋–ป๊ฒŒ ์‘๋‹ตํ•˜๋Š”์ง€

ํ…Œ์ŠคํŠธํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ

๋‹จ์ˆœํžˆ ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ๋†’ํžˆ๊ธฐ ์œ„ํ•ด ๋ชจ๋“  ๋ผ์ธ์„ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๋Š” ๊ฑด ๋‚ญ๋น„๋‹ค.

  • ๊ธฐ๋Šฅ์ด ๋‹จ์ˆœํ•œ ๊ฒƒ

  • UI์™€ ๊ด€๋ จ๋œ ํ…Œ์ŠคํŠธ

Jest

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ํ™˜๊ฒฝ ํ…Œ์ŠคํŠธ ๋„๊ตฌ

npm i -D jest @types/jest @swc/core @swc/jest \
  jest-environment-jsdom \
  @testing-library/react @testing-library/jest-dom

Jest ํŠน์ง•

Test Runner & Assertion ํ†ตํ•ฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

  • Zero config: ๋ณต์žกํ•œ ์„ค์ •์ด ์—†๋‹ค.

  • Snapshots ์ œ๊ณต: ์–ด๋– ํ•œ ์˜ค๋ธŒ์ ํŠธ ๋˜๋Š” ํ˜„์žฌ์˜ ์ƒํƒœ๋ฅผ ์บก์ณํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ

  • Isolated: ๋…๋ฆฝ์ ์ด๋ผ ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰ํ•˜๋Š” ์†๋„๋‚˜ ์„ฑ๋Šฅ์ด ๋›ฐ์–ด๋‚จ

  • Great API: ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ api ์ œ๊ณต

Test Runner โ†’ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ ํ›„ ๊ฒฐ๊ณผ ์ƒ์„ฑ

Assertion โ†’ ํ…Œ์ŠคํŠธ ์กฐ๊ฑด, ๋น„๊ต๋ฅผ ํ†ตํ•œ ํ…Œ์ŠคํŠธ ๋กœ์ง ๊ฒ€์ฆ

with Typescript

typescript(swc)์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์•„๋ž˜ ์ฝ”๋“œ์™€ ๊ฐ™์ด ์ถ”๊ฐ€ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: [
    '@testing-library/jest-dom/extend-expect',
  ],
  transform: {
    '^.+\\.(t|j)sx?$': ['@swc/jest', {
      jsc: {
        parser: {
          syntax: 'typescript',	
          jsx: true,
          decorators: true,
          },
        transform: {	
          react: {
            runtime: 'automatic',
          },
        },
      },
    }],
  },
};

BDD ์Šคํƒ€์ผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ƒ˜ํ”Œ

BDD ์Šคํƒ€์ผ โ†’ ๋Œ€์ƒ-ํ–‰์œ„ ์ค‘์‹ฌ์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์กฐ์งํ™”ํ•˜๋Š” ๋ฐฉ์‹.

describe('add', () => {
  context('with no argument', () => {
    it('return zero', () => {
      expect(add()).toBe(0);
    });
  });
  
  context('with only one argument', () => {
    it('return the same number', () => {
      expect(add(1)).toBe(1);
    });
  });

  context('with only two argument', () => {
    it('return the sum of two numbers', () => {
      expect(add(1, 2)).toBe(3);
    });
  });

  context('with only three argument', () => {
    it('return the sum of three numbers', () => {
      expect(add(1, 2, 3)).toBe(6);
    });
  });

  context('with mutliple argument', () => {
    it('return the sum of them numbers', () => {
      expect(add(1, 2, 3, 4, 5, 7, 8, 9, 0)).toBe(39);
    });
  });
});

setupTests.ts

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ํ•ด๋‹น ํŒŒ์ผ์˜ ์ฝ”๋“œ๋ฅผ ๋ชจ๋“  ํ…Œ์ŠคํŠธ ํŒŒ์ผ์— ํฌํ•จ์‹œํ‚ค๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

์ฝ”๋“œ์˜ ์ค‘๋ณต์„ ํ”ผํ•˜๊ณ  ๊น”๋”ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

Coverage

ํŠน์ • ํ…Œ์ŠคํŠธ์˜ ์ปค๋ฒ„๋ฆฌ์ง€ ์ง€ํ‘œ ํ™•์ธํ•˜๊ธฐ

npm test -- --testMatch="<rootDir>/src/[ํ…Œ์ŠคํŠธ ํŒŒ์ผ]" --collectCoverage --collectCoverageFrom="[ํ…Œ์ŠคํŠธ ๋Œ€์ƒ]"

Last updated