S4 Unit 7. TDD

나현·2022년 12월 2일
0

학습일지

목록 보기
46/53
post-thumbnail

💡 이번에 배운 내용

  • Section4. 사람과 기계가 모두 쉽고 빠르게 접근 가능한 Web App을 만들 수 있다.
  • Unit7. TDD: 테스트를 작성하고 테스트 통과 여부를 확인하며 개발하는 소프트웨어 개발 방법론인 TDD(Test-Driven-Development: 테스트 주도 개발)에 대해 알아보고, 실제 개발 환경에서는 어떻게 활용하는지 학습한다.

느낀점

테스트 케이스를 만들고 코드를 작성해가면 실습을 하고, 라이브러리를 사용해 결과를 html에 출력해보니 신기했다. 그리고 하나씩 통과해가는 맛이 있어 과제를 완성하니 유난히 뿌듯했다. 실제로 딥하게 TDD 방식으로 개발할 기회는 흔치 않겠지만, 나중에 기회가 된다면 이 방법으로도 개발해보면 어떨지 궁금하기도 한다. 과연?!
그리고 이번에는 블로깅이 오래 걸리지 않아 살 것 같다. 남은 시간 다른 공부를 좀 더 해야겠다...!


키워드

TDD, TDD의 개발 주기 3단계, mocha, chai, Testing Library, Jest, describe, it, test, assert, expect


학습내용

Ch1. TDD 방법론

TDD란?

TDD(Test-driven Development)의 개발자가 코드의 결과를 미리 정의하고, 이를 바탕으로 코드를 작성하는 소프트웨어 개발 방법론이다. 작은 단위의 테스트 케이스를 작성하고, 이를 통과하며 코드를 작성하는 것을 반복한다.

TDD의 개발 주기

다음은 TDD의 개발 주기 3단계이다.

  1. Write Failing Test: 실패하는 테스트 코드를 먼저 작성한다.
  2. Make Test Pass: 테스트 코드를 성공시키기 위한 실제 코드를 작성한다.
  3. Refactor: 중복 코드 제거, 일반화 등 리팩토링을 수행한다.

여기서 중요한 점을 정리하자면 다음과 같다.

  • 1단계를 끝내기 전에 2단계를 시작하지 않아야 한다.
  • 1단계에서 작은 단위의 테스트 케이스를 작성한다.
  • 2단계를 진행할 때 1단계의 테스트를 통과할 정도의 최소 코드만 작성해야 한다.

TDD의 장점

  • 테스트를 먼저 작성하면 코드를 어떻게 구성할지 고민하게 된다.
  • 결과적으로 버그가 더 적은 코드를 짤 수 있게 된다.
  • 불필요한 설계를 피할 수 있고, 테스트 코드의 요구 사항에 집중할 수 있다.
  • TDD를 따라 소프트웨어를 개발할 경우 결함을 50 ~ 90% 까지 감소시킬 수 있다.

TDD를 사용하는 이유

실제로 작업 속도의 문제로 대부분의 개발자는 코드를 먼저 작성하고 테스트를 하기 때문에 완전한 TDD를 따르는 경우는 많지 않다.

그럼에도 불구하고 TDD를 사용하는 이유는, 테스트 코드를 먼저 작성해서 시간이 오래 걸리는 것 같으나 오히려 버그를 줄여 소요 시간을 줄일 수 있기 때문이다.
일반적으로 개발하면 다양한 상황에 의해 여러 코드가 삽입, 수정, 삭제된다. 그러면 코드가 중복되거나 불필요한 코드가 생긴다. 이는 버그를 유발하거나 디버깅을 어렵게 한다.
소프트웨어 공학에서 가장 많은 비용이 드는 건 유지보수 단계이고, 이런 코드를 유지보수하기 위해서는 결국 후에 더많은 리소스를 투입해야 한다.

물론 현실적으로 TDD를 완전히 따르기는 힘들고, 학습 단계에서는 개발 실력 향상을 더 우선순위에 두어야 하므로 TDD를 학습하며 그 패턴과 실제 테스트 케이스 작성에 초점을 맞추도록 한다.

테스트 코드를 작성하는 방법

console.log를 통해 확인하는 것도 일종의 테스트이다.
그러나 자바스크립트 내장 기능 외에 테스트 프레임워크가 존재한다.
여러 테스트 프레임워크 중 mocha가 있으며, chai라는 라이브러리가 있다.
mocha와 chai를 사용하면 describe, it, assert, expect 등의 함수를 통해 여러 테스트 케이스를 작성하고 테스트 해볼 수 있다.

Ch2. React와 TDD

Testing Library, Jest

React에서 테스트시 Testing Library, Jest를 이용하며 각각 역할이 다르다.
create-react-app을 이용하여 React 프로젝트를 생성하면 자동으로 Testing Libarary를 이용할 수 있다.
Jest는 JavaScript의 Testing Framework / Test Runner이다.
테스트 파일을 자동으로 찾아 테스트를 실행하고, 테스트를 실행해 올바른 값을 가지고 있는지 함수를 이용하여 체크하고, 테스트의 성공 여부를 판단한다.
리액트에 대한 테스트를 진행할 때는 보통 한쪽만 사용하지않고 둘 다 사용한다.

1. React 기본 테스트 환경 파악

npx create-react-app 명령을 실행해 React 프로젝트를 만들고 package.json 파일을 확인하면, dependencies 안에 @testing이라는 접두어가 붙은 3개의 라이브러리를 확인할 수 있다. 또한 npm run test를 통해 테스트를 실행하도록 되어있다.
각 라이브러리에 대한 설명은 다음과 같다.

  • @testing-library/jest-dom : Jest-dom에서 제공하는 custom matcher를 사용할 수 있다.
  • @testing-library/react : 컴포넌트의 요소를 찾기 위한 query가 포함되어 있다.
  • @testing-library/user-event : click 등 사용자 이벤트에 이용된다.

또한 src 폴더 안에 기본으로 setupTests.js, App.test.js가 생성되어 있다.
테스트 파일명은 <파일명>.test.js과 같이 작성하면 Jest가 테스트파일로 판단하여 작동된다.
때문에 App.test.js 외에 다른 이름으로 테스트 파일을 작성할 수 있다.

그리고 App.test.js안의 내용을 확인하면 테스트 예시를 확인해 볼 수 있다.
거기서 사용하는 test 함수는 테스트를 실행할 때 필수로 작성하는 함수이다.
이는 it으로 대체하여 사용할 수 있다.
또한 여러개의 test 혹은 it 함수로 작성한 테스트 케이스는 describe함수로 묶어서 사용할 수 있다.
describe 함수 블록은 Test Suites, test/it 함수 블록은 Test(Test Case)라고도 한다.

2. 직접 만든 컴포넌트 테스트

임의의 컴포넌트를 작성하고, 테스트 파일을 작성한다.
(테스트 파일을 작성할 때는 보통 컴포넌트.js로 작성한 것과 똑같이 <컴포넌트명>.test.js 로 작성한다.)
테스트 파일을 작성했다면 터미널에 npm run start를 입력하여 테스트할 수 있고, 테스트를 통과했다면 PASS가 터미널 창에 출력된다.

import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';

it('MyComponent를 생성했는지 확인합니다.', () => {
	render(<MyComponent name="프론트엔드" />);
	const nameElement = screen.getByText(/프론트엔드 개발자/i);
	expect(nameElement).toBeInTheDocument();
})

위의 예시코드를 설명하자면 다음과 같다.

  • it: 첫번째 인자는 테스트 케이스에 대한 설명, 두번째 인자는 테스트 내용이 담긴 함수로 Jest에 포함된 함수이다. 이 it함수 자체가 테스트 케이스로, 함수명을 test로 대체할 수도 있다. test역시 Jest에 포함된 함수다.
  • render(): 테스트할 컴포넌트를 불러온다. 사용하기 위해 import 해야한다.
  • getByText(): screen메서드 중 하나로, 해당 텍스트가 작성되었는지 테스트한다. i는 대소문자를 구분하지 않겠다는 의미다. screen 메서드를 사용하려면 import 해야한다.
  • expect(): 인자로 받은 요소를 검사한다. Jest에 포함된 함수이다. 보통 뒤에 여러 메서드를 붙여서 테스트한다.
  • toBeInTheDocument(): expect 함수의 인자로 지정한 요소가 document.body에 존재하는지 체크한다. 여기서 이 toBeInTheDocument 함수는 matchers 함수라고도 한다. jest-dom 라이브러리에 포함되어 있는 함수이다.
import { fireEvent, render, screen } from '@testing-library/react';
import MyButton from './MyButton';

it('버튼 A를 클릭하면 버튼 A는 비활성화되고, 버튼B는 활성화되어야 합니다.', () => {
	render(<MyButton name="버튼명" />);
	const buttonBElement = screen.getByRole('button', { name: 'B' });
	fireEvent.click(buttonBElement);
	expect(buttonBElement).toBeDisabled();
})

위는 또다른 테스트 예시로, 버튼 A를 클릭하면 버튼 A는 비활성화되고, 버튼B는 활성화되는지 테스트하는 케이스다.
위 코드에 대한 설명은 다음과 같다.

  • getByRole() : 첫번째 인자로 button을 지정하고, 예제는 name으로 버튼명을 확인할 수 있기에 2개의 버튼(예제의 A,B)중 name을 이용해 B 버튼을 찾도록 두 번째 인자에 입력했다.
  • fireEvent.click() : fireEvent는 이벤트의 유무를 테스트할 수 있으며, .click()으로 클릭 이벤트를 지정한다. 인자로 테스트하고자 하는 요소를 전달하며, fireEvent를 사용하려면 import 해야한다.
  • toBeDisabled() : 앞의 요소가 disabled로 되어 있는지 확인한다. matchers 함수이다.
    앞에 not을 붙여 not.toBeDisabled()로 사용하면 disabled가 아닌지 테스트한다.

참고자료

1. chai 라이브러리 문서
🔗 https://www.chaijs.com/

profile
프론트엔드 개발자 NH입니다. 시리즈로 보시면 더 쉽게 여러 글들을 볼 수 있습니다!

0개의 댓글