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