Jest 로 테스트 코드 작성 후 테스트 해보기
Jest 개요
Jest 는 Meta 에서 개발한 테스팅 라이브러리이다. Jest 가 아닌 다른 테스팅 라이브러리의 경우 테스트를 진행하기 위해 Test Runner, Test Matcher, Test Mock 라이브러리를 각각 별도로 설치하여 사용해야 한다는 번거로움이 있었다. 하지만 Jest 의 경우, Jest 라이브러리 하나만 설치하면 테스트를 진행할 수 있다는 장점이 있어 자바스크립트 개발자에게 많은 사랑을 받고있는 테스팅 라이브러리이다. 그러면 이제부터 Jest 를 어떻게 사용하는 지에 대해 알아보도록 하자.
Jest 설치
- 프로젝트 생성
mkdir jest-test && cd jest-test npm init
- Jest 설치
npm install --save -dev jest
package.json
script 수정
"script": { "test": "jest" }
jest-cli
설치 (선택 사항)
하나의 테스트 파일만 테스트를 진행하고 싶으면 설치해야 한다. 혹시 권한이 없다는 메시지가 뜨면 앞에
sudo
키워드를 붙여서 설치해주자.npm install -g jest-cli
주의사항
Jest 라이브러리는 아래의 명명 규칙을 따르는 파일들만 찾아 테스트를 진행하게 된다.
__tests__
폴더에.js
혹은ts
로 끝나는 파일
.test.js
혹은.test.ts
로 끝나는 파일
.sepc.js
혹은.spec.ts
로 끝나는 파일
이에 맞추어 파일 이름을 명명하지 않고 테스트 파일을 작성 시에는, 라이브러리가 인식하지 못한다.
아래는 명명 규칙에 따르는 파일이 하나도 없을 때 발생하는 오류이다. 테스트를 할 항목을 찾을 수 없다는 에러 메시지를 반환하며, 파일 명을 어떻게 명명해야 할 지에 대한 정규식도 알려주고 있다.
npm test > jest@1.0.0 test > jest No tests found, exiting with code 1 Run with `--passWithNoTests` to exit with code 0 In /Users/jeonbeomgu/Desktop/Blog/jest 4 files checked. testMatch: **/__tests__/**/*.[jt]s?(x), **/?(*.)+(spec|test).[tj]s?(x) - 0 matches testPathIgnorePatterns: /node_modules/ - 4 matches testRegex: - 0 matches Pattern: - 0 matches
일반적으로는
[테스트 할 파일 이름].test.js
의 형태로 명명한다.테스트 코드 작성
Jest 를 사용하면 E2E(End-to-End) 테스트도 가능하지만, 오늘은 유닛 테스트에 초점을 맞추어 예제를 살펴볼 예정이다.
예제 테스트 코드를 위한 파일 준비
calc.js
exports.sum = (a, b) => { return a + b; }; exports.substract = (a, b) => { return a - b; }; exports.divide = (a, b) => { if (b === 0) { throw new Error('Cannot divide by zero'); } return a / b; }; exports.multiply = (a, b) => { return a * b; };
test.js
- 테스트 코드를 작성할 파일
test.js
파일에서는 calc.js
모듈 이외에 다른 내용도 테스트를 진행할 예정이라 편의상 이름을 test.js
로 작명하였다.기본 테스트 코드 구조
Jest 테스트 코드 작성시에는 아래의 기본적인 구조로 작성을 하면 된다.
test('test description', () => { expect('Actual Result').toXXX('Expected Result'); // 실제 값과 기대 값이 다른 지 확인하고 싶을 때는 not 키워드를 사용하여 테스트 가능 expect('Actual Result').not.toXXX('Expected Result'); });
toXXX
자리에는 적절한 Matcher 함수명을 넣어주면 된다. 이 함수들의 종류는 아래에서 알아보도록 하자.자주 사용되는 Matcher 종류 및 예제
- toBe
toBe
Matcher 의 경우에는 실제 실행한 값과 기대하는 값이 같은 지 판별할 때 사용되는 Matcher 이다. 주의할 점은 값을 비교하는 것이기 때문에, 객체를 비교할 때에는 아래에 있는 toEqual
Matcher 를 사용해야 한다.테스트 코드
test('1 + 2 = 3', () => { expect(calc.sum(1, 2)).toBe(3); });
결과
npm test > jest@1.0.0 test > jest PASS ./test.js ✓ 1 + 2 = 3 (2 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 0.333 s, estimated 1 s Ran all test suites.
- toEqual
toEqaul
Matcher 는 위에서 이야기 했듯이 객체를 비교할 때 쓰는 Matcher 이다. 자세한 내용은 테스트 코드로 toBe
Matcher 와 직접 비교하면서 알아보자.테스트 코드
// toBe 사용 - Failed test('compare two obejcts - toBe', () => { const person1 = { name: 'Daniel', gender: 'M', age: 25 }; const person2 = { name: 'Daniel', gender: 'M', age: 25 }; expect(person1).toBe(person2); }); // toEqual 사용 - Passed test('compare two obejcts - toEqual', () => { const person1 = { name: 'Daniel', gender: 'M', age: 25 }; const person2 = { name: 'Daniel', gender: 'M', age: 25 }; expect(person1).toEqual(person2); });
위 테스트 코드를 실행해보면 아래와 같은 결과가 나온다. 결과를 보면 알 수 있듯이, 객체가 같은 지 비교할 때
toBe
Matcher 를 쓰게 되면 테스트가 실패 하지만, toEqual
Matcher 를 사용하면 테스트를 통과하는 것을 볼 수 있다. 따라서, 객체를 비교할 때에는 toEqaul
메소드를 사용해야 한다는 사실을 기억하자.결과
npm test > jest@1.0.0 test > jest FAIL ./test.js ✕ compare two obejcts - toBe (5 ms) ✓ compare two obejcts - toEqual (1 ms) ● compare two obejcts - toBe expect(received).toBe(expected) // Object.is equality If it should pass with deep equality, replace "toBe" with "toStrictEqual" Expected: {"age": 25, "gender": "M", "name": "Daniel"} Received: serializes to the same string 8 | const person1 = { name: 'Daniel', gender: 'M', age: 25 }; 9 | const person2 = { name: 'Daniel', gender: 'M', age: 25 }; > 10 | expect(person1).toBe(person2); | ^ 11 | }); 12 | 13 | test('compare two obejcts - toEqual', () => { at Object.toBe (test.js:10:19) Test Suites: 1 failed, 1 total Tests: 1 failed, 1 passed, 2 total Snapshots: 0 total Time: 0.363 s, estimated 1 s Ran all test suites.
- toBeTruthy / toBeFalsy
이 두 가지 Matcher 는 실제 값에 대한
true
와 false
에 대한 값을 테스트한다. 자바스크립트에서는 변수의 타입에 대해 엄격하지 않기에, true
와 false
가 boolean
타입에 대해서만 한정되지 않는다. 이 말은 모든 타입에 대해 true
와 false
값을 가질 수 있따는 말이다. 예를 들면, 숫자 0
의 경우 false
를 나타내고, 문자열 '0'
에 대해서는 true
를 나타내는 것을 볼 수 있다.변수에 대한
true/false
예제 및 결과// number 0 if (0) { console.log('number 0 is true'); } else { console.log('number 0 is false'); } // string 0 if ('0') { console.log('string 0 is true'); } else { console.log('string 0 is false'); } // result number 0 is false string 0 is true
이를 알고, 이에 근거하여 여러 가지 경우의 테스트 코드를 작성해보자.
테스트 코드
test('to be truthy and falsy test', () => { // False const num1 = 0; const emptyStr = ''; const boolFalse = false; expect(num1).toBeFalsy(); expect(emptyStr).toBeFalsy(); expect(boolFalse).toBeFalsy(); // True const numStr = '0'; const num2 = 1; const boolTrue = true; expect(num2).toBeTruthy(); expect(numStr).toBeTruthy(); expect(boolTrue).toBeTruthy(); });
아래 결과와 같이 숫자
0
은 false
를, 문자열 '0'
은 true
를 나타내는 것을 볼 수 있다. 또한, 빈 문자열의 경우는 false
를 반환함을 알 수 있다.결과
npm test > jest@1.0.0 test > jest PASS ./test.js ✓ to be truthy and falsy test (3 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 0.455 s, estimated 1 s Ran all test suites.
- toHaveLength / toContain
이름에서 알 수 있듯이
toHaveLength
는 실제 값 혹은 문자열의 길이, toContain
은 실제 값이 특정 값 혹은 문자열을 포함하고 있는 지에 대해 테스트 하는 Matcher 이다. 아래의 테스트 코드를 통해 각각 문자열, 배열의 경우에 대해 테스트를 진행해보자.테스트 코드
test('to have length and to contain test - string', () => { const str = 'Hello World'; expect(str).toHaveLength(11); expect(str).toContain('H'); expect(str).toContain('h'); // Fail }); test('to have length and to contain test - array', () => { const arr = [1, 2, 3]; expect(arr).toHaveLength(3); expect(arr).toContain(2); expect(arr).toContain('2'); // Fail });
아래 결과에서 알 수 있듯이
toContain
Matcher 를 사용 시에 주의해야 할 사항이 있다. 문자열의 경우 대소문자를 구분하며, 배열에서 값을 포함하는 지 여부를 테스트 할 때에는 타입도 비교한다는 사실이다.결과
npm test > jest@1.0.0 test > jest FAIL ./test.js ✕ to have length and to contain test - string (4 ms) ✕ to have length and to contain test - array (2 ms) ● to have length and to contain test - string expect(received).toContain(expected) // indexOf Expected substring: "h" Received string: "Hello World" 39 | expect(str).toHaveLength(11); 40 | expect(str).toContain('H'); > 41 | expect(str).toContain('h'); | ^ 42 | }); 43 | 44 | test('to have length and to contain test - array', () => { at Object.toContain (test.js:41:15) ● to have length and to contain test - array expect(received).toContain(expected) // indexOf Expected value: "2" Received array: [1, 2, 3] 46 | expect(arr).toHaveLength(3); 47 | expect(arr).toContain(2); > 48 | expect(arr).toContain('2'); | ^ 49 | }); 50 | 51 | // test('to match test', () => {}); at Object.toContain (test.js:48:15) Test Suites: 1 failed, 1 total Tests: 2 failed, 2 total Snapshots: 0 total Time: 0.541 s, estimated 1 s Ran all test suites.
- toMatch
toMatch
Matcher 는 문자열이 정규식을 만족하는 지 판별할 때 사용하는 Matcher 이다. 마찬가지로 아래의 테스트 코드를 통해 알아보자.테스트 코드
test('to match test', () => { const str = '010-1234-5678'; expect(str).toMatch(/^\d{3}-\d{4}-\d{4}$/); });
위 테스트 코드는 휴대폰 번호가 올바르게 작성 되었는 지 확인하는 테스트 코드로, 올바르게 작성되어 아래의 결과처럼 테스트를 통과하는 것을 볼 수 있다.
결과
npm test > jest@1.0.0 test > jest PASS ./test.js ✓ to match test (4 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 0.708 s, estimated 1 s Ran all test suites.
- toThrow
마지막으로
toThrow
의 경우에는 함수를 호출했을 때 예외가 발생하는 지 알려주는 Matcher 이다. 이 Matcher 에 인자를 넘기지 않으면 단순히 예외 발생 여부만 체크하고, 문자열을 넘기면 예외 메시지 까지 비교할 수 있다. 다만 예외 메시지가 정확하게 같은 지 비교하지는 않고 넘겨진 문자열이 예외 메시지에 포함되는 지를 판별한다. 이 내용은 Jest 공식 문서 에 설명되어 있으니 한 번 읽어보도록 하자.테스트 코드
test('to throw test 1', () => { expect(calc.divide(1, 0)).toThrow(); }); test('to throw test 2', () => { expect(() => calc.divide(1, 0)).toThrow(); expect(() => calc.divide(1, 0)).toThrow('Cannot divide by zero'); expect(() => calc.divide(1, 0)).toThrow('divide'); });
테스트 코드를 실행하면 알 수 있듯이,
toThrow
를 사용할 때는 주의해야 할 사항이 하나 있다. expect
에 함수를 넘길 때 반드시 함수로 한 번 감싸서 전달해야 한다는 것이다. 그렇지 않으면 에러가 나는 것을 확인할 수 있다. 이 또한 Jest 공식 문서 에 언급된 내용으로, 꼭 지켜주어야 한다.결과
npm test > jest@1.0.0 test > jest FAIL ./test.js ✕ to throw test 1 (1 ms) ✓ to throw test 2 (5 ms) ● to throw test 1 Cannot divide by zero 9 | exports.divide = (a, b) => { 10 | if (b === 0) { > 11 | throw new Error('Cannot divide by zero'); | ^ 12 | } 13 | return a / b; 14 | }; at Object.<anonymous>.exports.divide (calc.js:11:11) at Object.divide (test.js:57:15) Test Suites: 1 failed, 1 total Tests: 1 failed, 1 passed, 2 total Snapshots: 0 total Time: 0.5 s, estimated 1 s Ran all test suites.
결론
최근 TDD 방식으로 개발하는 회사도 많아지고, 테스트 자동화도 중요한 시점에서 Jest 테스팅 라이브러리를 아는 것은 자바스크립트 개발자에게 중요하다고 생각한다. 오늘은 Jest 라이브러리의 기본적인 사용법을 알아보았고, 추후에 기회가 된다면 조금 더 심화적인 내용을 다루어보고자 한다. 위에서 언급하지 않은 기능들도 많으니 더 알고 싶다면 Jest 공식 문서 를 참고하도록 하자. 이외에도
express
환경에서 사용할 수 있는 다른 테스트 툴인 Supertest
도 있으니 나중에 시간이 난다면 공부를 하고 포스팅을 해보도록 하겠다.