λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ—‚οΈ μŠ€ν„°λ”” ν™œλ™/πŸ“— 면접을 μœ„ν•œ CS 전곡지식 λ…ΈνŠΈ

[마무리] ν”„λ‘ νŠΈμ—”λ“œ λ‹¨μœ„ ν…ŒμŠ€νŠΈ

by πŸ’Ύκ³ κ΅¬λ§ˆλ§›νƒ•λ¨Ήκ³ μ‹Άλ‹€ 2024. 10. 30.
728x90

πŸ—‚οΈ λ‹¨μœ„ ν…ŒμŠ€νŠΈ

μ†Œν”„νŠΈμ›¨μ–΄μ˜ νŠΉμ • λͺ¨λ“ˆμ΄λ‚˜ κΈ°λŠ₯이 μ˜¬λ°”λ₯΄κ²Œ μž‘λ™ν•˜λŠ”μ§€λ₯Ό κ²€μ¦ν•˜λŠ” ν…ŒμŠ€νŠΈλ‘œ, μ½”λ“œμ˜ 일뢀λ₯Ό λ…λ¦½μ μœΌλ‘œ ν…ŒμŠ€νŠΈν•˜λŠ” 것

 

ν”„λ‘ νŠΈμ—”λ“œ ν…ŒμŠ€νŠΈ μ’…λ₯˜

  • λ‹¨μœ„ ν…ŒμŠ€νŠΈ (Unit Test) : μ½”λ“œμ˜ μž‘μ€ λ‹¨μœ„λ‚˜ κ°œλ³„ ν•¨μˆ˜λ₯Ό λ…λ¦½μ μœΌλ‘œ ν…ŒμŠ€νŠΈν•˜λ©°, λΉ λ₯΄κ³  νš¨μœ¨μ μ΄λ‹€.
  • μŠ€λƒ…μƒ· ν…ŒμŠ€νŠΈ (Snapshot Test) : 주둜 UI의 λ³€κ²½ μ—¬λΆ€λ₯Ό ν™•μΈν•˜μ—¬, μ˜ˆμƒλœ UI μƒνƒœμ™€ ν˜„μž¬ UI μƒνƒœκ°€ μΌμΉ˜ν•˜λŠ”μ§€ λΉ„κ΅ν•œλ‹€.
  • 쒅단 κ°„(e2e) ν…ŒμŠ€νŠΈ (End-to-End Test) : μ‚¬μš©μž μž…μž₯μ—μ„œ 전체 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ 흐름을 ν…ŒμŠ€νŠΈν•˜μ—¬ μ‹œμŠ€ν…œμ΄ ν†΅ν•©λœ μƒνƒœμ—μ„œ μž‘λ™ν•˜λŠ”μ§€ ν™•μΈν•œλ‹€.

λ‹¨μœ„ ν…ŒμŠ€νŠΈμ˜ μž₯점 : 버그λ₯Ό μ΄ˆκΈ°μ— μž‘μ•„λ‚΄κ³ , μ½”λ“œ λ³€κ²½ μ‹œ μ˜ˆμƒν•˜μ§€ λͺ»ν•œ 였λ₯˜λ₯Ό 방지, μœ μ§€ λ³΄μˆ˜κ°€ μš©μ΄ν•˜λ©°, μ½”λ“œμ˜ μ•ˆμ •μ„±μ„ λ†’μ—¬μ€€λ‹€.

 


πŸ₯‡ F.I.R.S.T

λ‹¨μœ„ ν…ŒμŠ€νŠΈ μ½”λ“œ μž‘μ„± μ‹œ μ§€μΌœμ•Ό ν•  원칙

  • Fast : ν…ŒμŠ€νŠΈλŠ” λΉ λ₯΄κ²Œ μˆ˜ν–‰ν•΄μ•Όν•¨. 느린 ν…ŒμŠ€νŠΈλŠ” 개발 흐름을 λ°©ν•΄ν•˜κ³ , 싀행이 꺼렀질 수 μžˆμŠ΅λ‹ˆλ‹€.
  • Isolated : λ‹¨μœ„ ν…ŒμŠ€νŠΈλŠ” μ„œλ‘œ λ…λ¦½μ μœΌλ‘œ μ‹€ν–‰λ˜μ–΄μ•Όν•œλ‹€. μ„œλ‘œ μ˜μ‘΄ν•˜μ§€ μ•Šλ„λ‘ μ„€μ •ν•œλ‹€.
  • Repeatable : ν™˜κ²½μ— 영ν–₯λ°›μ§€μ•Šμ•„ μ‹€ν–‰ν•  λ•Œλ§ˆλ‹€ μΌκ΄€λœ κ²°κ³Όλ₯Ό λ‚΄μ•Όν•œλ‹€.
  • Self-validating : ν…ŒμŠ€νŠΈλŠ” κ²°κ³Όκ°€ ‘성곡’ λ˜λŠ” ‘μ‹€νŒ¨’둜 μžλ™μœΌλ‘œ ν‰κ°€λ˜λ„λ‘ μž‘μ„±ν•΄μ•Όν•œλ‹€.
  • Timely/Thorough : ν…ŒμŠ€νŠΈλ₯Ό 미리 μž‘μ„±ν•˜μ—¬ μ½”λ“œμ˜ κΈ°λŠ₯κ³Ό λ™μž‘μ„ κ²€μ¦ν•˜λŠ” 방식을 μ‚¬μš©ν•΄μ•Όν•œλ‹€. => TDD(κ΅¬ν˜„ν•˜κ³ μž ν•˜λŠ” κΈ°λŠ₯을 미리 ν…ŒμŠ€νŠΈλ‘œ μž‘μ„±)

 

πŸ₯ˆ DAMP

Descriptive And Meaningful Phrases : 읽기 쉽고 직관적인 μ½”λ“œ μž‘μ„±

  • 의미 있고 μ„œμˆ μ μΈ ν‘œν˜„ μ‚¬μš©: μ½”λ“œκ°€ λ‹¨μˆœνžˆ μž‘λ™ν•˜λŠ” 것을 λ„˜μ–΄, μ˜λ„λ₯Ό λͺ…ν™•ν•˜κ²Œ 전달할 수 μžˆμ–΄μ•Όν•œλ‹€.
  • DRY(Don't Repeat Yourself) μ›μΉ™κ³Όμ˜ 차이: ν…ŒμŠ€νŠΈ μ½”λ“œμ—μ„œλŠ” 쀑볡을 μ΅œμ†Œν™”ν•˜κΈ°λ³΄λ‹€ μ½”λ“œκ°€ μ„€λͺ…적이고 직관적인 것이 더 μ€‘μš”ν•˜λ‹€.

μ˜ˆμ‹œ

쀑볡을 μ œκ±°ν•œ κΉ”λ”ν•œ μ½”λ“œκ°€ μ•„λ‹Œ, 가독성과 λͺ…확성을 높인 μ½”λ“œκ°€ μž₯기적으둜 μœ μ§€ λ³΄μˆ˜μ™€ 이해에 μœ λ¦¬ν•¨.

μ•„λž˜ μ½”λ“œλŠ” μ–΄λ–€ μ»΄ν¬λ„ŒνŠΈμ—μ„œ νŠΉμ • λ²„νŠΌμ„ ν΄λ¦­ν–ˆμ„ λ•Œ νŠΉμ • ν•¨μˆ˜κ°€ μ˜¬λ°”λ₯Έ λ§€κ°œλ³€μˆ˜λ‘œ ν˜ΈμΆœλ˜λŠ”μ§€λ₯Ό ν™•μΈν•˜λŠ” ν…ŒμŠ€νŠΈ μ½”λ“œμ΄λ‹€.

 

// 1번
it('μ„€λͺ…을 μƒλž΅ν•˜λ©΄ 이해가 λ˜μ‹œλ‚˜μš”?', async () => {
    const user = userEvent.setup();
    render(<Component {...preDefinedProps} />);

    const buttonElement = screen.getByRole('button', { name: 'λ²„νŠΌμž…λ‹ˆλ‹€' });
    await user.click(buttonElement);

    expect(doSomething).toBeCalledWith(1234);
});
// 2번
it('μ„€λͺ…이 없어도 λŒ€μΆ© λŠλ‚Œμ€ μ˜€μ‹œμ£ ?', async () => {
    const something = 1234;
    const doSomething = vi.fn();
    const user = userEvent.setup();
    render(<Component {...preDefinedProps} something={something} doSomething={doSomething} />);

    const buttonElement = screen.getByRole('button', { name: 'λ²„νŠΌμž…λ‹ˆλ‹€' });
    await user.click(buttonElement);

    expect(doSomething).toBeCalledWith(1234);
});

 

πŸ₯‰ Given-When-Then

Given-When-Then κ΅¬μ‘°λŠ” BDD의 쀑심인 μ‚¬μš©μž ν–‰μœ„λ₯Ό 기반으둜 ν•œ ν…ŒμŠ€νŠΈ μ‹œλ‚˜λ¦¬μ˜€λ₯Ό μ •μ˜ν•  수 μžˆλ„λ‘ 도와쀀닀.

 

  • Given: ν…ŒμŠ€νŠΈλ₯Ό ν•˜κΈ° μœ„ν•΄ μ„ΈνŒ…ν•˜λŠ” 주어진 ν™˜κ²½
  • When: ν…ŒμŠ€νŠΈλ₯Ό ν•˜κΈ° μœ„ν•œ 쑰건으둜 ν”„λ‘ νŠΈμ—”λ“œμ—μ„  μ‚¬μš©μžμ™€μ˜ μƒν˜Έμž‘μš©μΈ κ²½μš°λ„ 많음
  • Then: μ˜ˆμƒ κ²°κ³Όλ₯Ό λ‚˜νƒ€λ‚΄λ©° μ˜λ„λŒ€λ‘œ λ™μž‘ν•˜λŠ”μ§€ 검증 및 확인할 수 있음

Given-When-Then의 μž₯점

  • ν…ŒμŠ€νŠΈ μ½”λ“œκ°€ λͺ…ν™•ν•˜κ³  읽기 μ‰¬μ›Œμ Έ, ν˜‘μ—… μ‹œ ν…ŒμŠ€νŠΈ μ˜λ„λ₯Ό μ‰½κ²Œ κ³΅μœ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 둜직의 흐름에 따라 μžμ—°μŠ€λŸ½κ²Œ μ‹œλ‚˜λ¦¬μ˜€λ₯Ό μ„€μ •ν•˜κ³ , 쑰건과 κ²°κ³Όλ₯Ό 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

it('λ²„νŠΌμ„ 1회 ν΄λ¦­ν•˜λ©΄ 1번 ν΄λ¦­ν–ˆλ‹€λŠ” 문ꡬ가 λ…ΈμΆœλœλ‹€', async () => {
	const user = userEvent.setup();
	render(<Component />);

	const buttonElement = screen.getByRole('button', { name: 'μ—¬κΈ°λ₯Ό λˆŒλŸ¬λ³΄μ„Έμš”' });
	await user.click(buttonElement);

  	expect(screen.getByText('λ²„νŠΌμ„ 1번 ν΄λ¦­ν–ˆμŠ΅λ‹ˆλ‹€.')).toBeInTheDocument();
});

 


πŸš₯ κ°œλ³„ ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ˜ λͺ©μ μ€ λͺ…ν™•νžˆ

it('λ²„νŠΌ 클릭 μ‹œ μ’‹μ•„ν•˜λŠ” 과일이 λ°”λ‚˜λ‚˜λ‘œ 바뀐닀', async () => {
  const user = userEvent.setup();
  render(<WordWithButton />);

  await user.click(screen.getByRole('button', { name: 'μ’‹μ•„ν•˜λŠ” 과일 λ°”κΎΈκΈ°' }));

  expect(screen.getByText(/λ°”λ‚˜λ‚˜/i)).toBeInTheDocument();
});

μ˜λ„ν•˜μ§€ μ•Šμ€ κ³³μ—μ„œ λ°”λ‚˜λ‚˜κ°€ 좜λ ₯λ˜μ—ˆμ„ 수 μžˆλ‹€.

λ™μž‘μ— λŒ€ν•œ κ²°κ³Όλ₯Ό κ²€μ¦ν•˜λŠ”λ° λ°”λ‚˜λ‚˜λŠ” 좜λ ₯λ˜μ—ˆμ§€λ§Œ μ‹€μ œ μƒνƒœλ³€ν™”κ°€ λ‚˜νƒ€λ‚¬λŠ”μ§€ 확인할 수 μ—†λ‹€.

 

it('λ²„νŠΌ 클릭 μ‹œ heading μ˜μ—­μ˜ 문ꡬ가 λ°”λ‚˜λ‚˜λ₯Ό μ’‹μ•„ν•œλ‹€λŠ” λ‚΄μš©μœΌλ‘œ λ³€κ²½λœλ‹€', async () => {
  const user = userEvent.setup();
  render(<WordWithButton />);

  await user.click(screen.getByRole('button', { name: 'μ’‹μ•„ν•˜λŠ” 과일 λ°”κΎΈκΈ°' }));

  expect(screen.getByRole('heading', { name: 'λ‚˜λŠ” λ°”λ‚˜λ‚˜λ₯Ό μ’‹μ•„ν•œλ‹€!' })).toBeInTheDocument();
});

heading μ˜μ—­μ— λ‚˜νƒ€λ‚˜λŠ”μ§€λ₯Ό κ²€μ¦ν•˜κ³  μžˆμ–΄ λͺ©μ μ΄ λͺ…ν™•ν•˜λ‹€.

μž‘μ„±ν•˜λŠ” ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€κ°€ μ •ν™•ν•˜κ²Œ μ–΄λ–€ 것을 검증할 것인지 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ˜ λͺ©μ μ„ μƒκ°ν•˜λ©° μ½”λ“œμ™€ expectλ₯Ό μž‘μ„±ν•΄μ•Όν•œλ‹€.

 

 

 

 

μ°Έκ³  : https://techblog.woowahan.com/17404/

728x90