TDD(Test-driven Development)는 코드를 작성하기 테스트 로직을 작성하는 소프트웨어 개발 방법론입니다. 바람직하다고 생각하는 코드의 결과를 정의한 후 개발을 이어나갑니다. 작은 단위의 테스트 케이스를 작성하고 이를 통과하는 코드를 작성하는 과정을 반복합니다.
TDD의 개발 주기는 다음과 같습니다.
예를 들어 양의 정수를 전달 받아 그 숫자가 짝수인지 확인하는 테스트 케이스를 만든다고 가정할 경우, 숫자가 홀수 인경 우에 실패하는 로직을 작성하고 테스트가 성공하는 코드를 최소화해서 작성합니다. 그리고 최종적으로 중복된 코드를 제거하는 등 리팩토링을 진행합니다. 테스트를 먼저 작성하는 것은 필연적으로 코드를 어떻게 구성할지 고민하게 된다는 것을 의미하고, 결과적으로 버그가 더 적은 코드를 짤 수 있게 됩니다.
일반적으로 TDD를 따라 소프트웨어를 개발할 경우 그렇지 않은 경우보다 결함을 50 ~ 90% 까지 감소시킬 수 있다고 합니다. 하지만 대부분의 개발자들이 생각하고 일하는 방식과 일치하지 않기 때문에 생각보다 많은 개발자들이 테스트를 작성하지 않는다고 합니다. 테스트를 작성하는 것보다 바로 코드를 입력하는 것이 작업 초기에는 생산선이 높아 보이기 때문입니다
물론 머리 속에서 버그가 발생하지 않는 좋은 코드를 즉각적으로 생각해서 코드를 짠다면 가장 이상적 이겠지만, 예상치 못한 버그 발생 시 그 버그를 해결하는데 사용되는 기회비용과 추후 유지보수에 들어가는 시간을 고려한다면, 테스트 주도 개발이 더 효율적일 수 있습니다.
여러 개발자들이 더 나은 테스트를 작성하기 위해 많은 테스트 오픈소스 프레임워크를 제작했습니다. 이후 진행할 Test Builder 과제에서는 mocha라는 테스트 프레임워크와 chai라는 라이브러리를 사용합니다.
Mocha는 테스트 프레임워크 중 하나로 test, it, describe와 같은 테스팅 함수를 제공합니다.
첫번째 전달인자는 테스트의 설명이며 두번째 전달인자로 함수를 전달합니다. 함수 내부에서 실행된 코드가 올바르지 않을 경우 테스트는 실패합니다.
test('1 더하기 1은 2', ()=> {
expect(1 + 1).toBe(2);
});
it함수나 test함수가 여러개 일경우 describe로 묶어 줄 수 있습니다.
describe('더하기 테스트' , () => {
it('1 더하기 1은 2', ()=> {
expect(1 + 1).toBe(2);
});
it('2 더하기 2는 2', ()=> {
expect(2 + 2).toBe(4);
});
});
chai는 Node.js 기반의 Assertion 라이브러리로 Should, Expect, Assert와 같은 테스트에 필요한 헬퍼 함수들을 제공합니다. 자세한 API는 Chai 공식 홈페이지에서 확인 가능합니다.
헬퍼 함수를 불러와 필요한 기능을 사용할 수 있습니다.
const chai = require('chai');
const expect = chai.expect;
const assert = chai.assert;
const should = chai.should();
expect, should은 둘다 체인을 통한 Assertion을 구현하지만 사용방법이 조금 다릅니다.
describe('#Expect Test', function () {
it('expect - Array', function () {
let arr = [1, 2, 3, 4, 5];
expect(arr).to.have.lengthOf(5); //array length
expect(arr).to.be.not.empty; //empty
expect(arr).to.have.ordered.members([1, 2, 3, 4, 5]); //arr === members
});
});
describe('#Should Test', function () {
it('should - Array', function () {
let arr = [1, 2, 3, 4, 5];;
arr.should.have.lengthOf(5); //array length
arr.should.be.not.empty; //empty
arr.should.have.ordered.members([1, 2, 3, 4, 5];); //arr === members
});
it('should - String', function () {
let str = 'Awesome!!';
str.should.be.a('String'); //str type
str.should.equal('Awesome!!'); //str === 'Awesome!!'
str.should.have.lengthOf(7, 'Why fail?'); //Error
});
it('should - Object', function () {
let obj = {
assertion: ['assert', 'expect', 'should'],
framework: 'mocha'
};
obj.should.have.property('assertion').with.lengthOf(3); //assertion value length
obj.should.have.all.keys('framework', 'assertion'); //obj key === keys
});
});
assert는 참인지 거짓인지 확인하여 거짓인 경우에 에러를 발생 시킵니다. assert 함수를 직접 구현하면 아래의 코드와 같은 기능을 합니다.
let assert = function (isTrue) {
if (!isTrue) {
throw new Error("Test failed");
}
};
describe('#Assert Test', function () {
it('assert - Array', function () {
let str = 'Awesome!!';
let obj = {
assertion: ['assert', 'expect', 'should'],
framework: 'mocha'
};
assert.equal(str, 'Awesome!!'); //str === 'Awesome!!'
assert.typeOf(str, 'String'); //str type
assert.lengthOf(obj.assertion, 3); //assertion value length
});
});
<!DOCTYPE html>
<html>
<head>
<!-- 결과 출력에 사용되는 mocha css를 불러옵니다. -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css">
<!-- Mocha 프레임워크 코드를 불러옵니다. -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js"></script>
<script>
mocha.setup('bdd'); // 기본 셋업
</script>
<!-- chai를 불러옵니다 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script>
<script>
// chai의 다양한 기능 중, assert를 전역에 선언합니다.
let assert = chai.assert;
</script>
</head>
<body>
<script>
function add(num1, num2) {
/* 코드를 여기에 작성합니다. 지금은 빈칸으로 남겨두었습니다. */
}
</script>
<!-- 테스트(describe, it...)가 있는 스크립트를 불러옵니다. -->
<script src="test.js"></script>
<!-- 테스트 결과를 id가 "mocha"인 요소에 출력하도록 합니다.-->
<div id="mocha"></div>
<!-- 테스트를 실행합니다! -->
<script>
mocha.run();
</script>
</body>
</html>
React에서 테스트 코드를 작성할 때, Mocha, Chai 대신 Testing Library, Jest를 이용해서 할 수 있습니다.
Testing Libarary에서 React용 React Testing Library을 제공하며 create-react-app을 이용하여 React 프로젝트를 생성하면 자동으로 Testing Libarary를 이용할 수 있습니다.
Jest는 JavaScript의 Testing Framework/Test Runner로써, 테스트 파일을 자동으로 찾아 테스트를 실행하고, 테스트를 실행한 결과 기대만큼 올바른 값을 가지고 있는지 함수를 이용하여 체크하여 테스트가 성공인지 실패인지를 판단해 줍니다.
CRA를 설치하고나면 package.json에 설치된 다음과 같은 dependecies를 확인할 수 있습니다.
src 폴더 안을 확인해 보면, 기본적으로 setupTests.js 와 App.test.js 라는 이름의 파일을 확인할 수 있습니다. npm run test 명령어로 test를 실행시킬 수 있습니다.
테스트를 실행하면 위와 같은 메시지가 나타납니다. 메시지를 보면 키보드 키에 따라 다양한 처리를 할 수 있다는 것을 알 수 있습니다.
테스트를 실행 시키면 위와 같이 테스트에 대한 정보를 확인할 수 있습니다.
App.test.js로 돌아가서 코드를 보면 @testing-library/react 라이브러리로 부터 render, screen을 불러와 사용합니다. test함수를 사용 방식은 Mocha와 비슷합니다.
1. render() : 테스트를 진행하고자하는 컴포넌트를 render 함수의 인자로 전달합니다.
2. screen : 스크린은 다양한 메소드를 가지고 있는데 그 중에서 getByText는 인자로 받은 문자열이 해당 컴포난트에 있는지 확인합니다.
3. expect : Jest의 함수로 체인을 통해 테스트를 검증합니다.
4. toBeInTheDocument : jest-dome에 포함된 Custom Matcher입니다.
Custom Matcher을 별도로 import 해주지 않았는데 사용할 수 있는 이유가 무엇일까요? setupTests.js 파일을 확인해보면 jest-dom 라이브러리를 import하는 것을 알 수 있습니다.
https://ko.javascript.info/testing-mocha#ref-226
https://www.chaijs.com/
https://kdydesign.github.io/2017/06/15/Mocha-step-02/