단순한 자바스크립트 개발에서는 단위 테스트가 필요하지 않았습니다.
오히려 테스트를 기반으로 개발한다는 개념 자체가 개발을 더욱 어렵게 했었죠. 뭐, 저는 그랬습니다.
하지만 자바스크립트 개발이 점차 복잡해지면서 작성한 함수가 잘 동작하고 제대로된 값을 반환하는지 검증하는 과정은 간단한 기능에서조차 필수가 되어버린 듯합니다.
그리하여 살펴볼 Mocha(모카)는 테스트 러너를 지원하는 테스트 프레임워크 입니다.
자체 Assertion(어써션)은 지원하지 않으며, 필요한 Assertion 라이브러리를 가져와 사용할 수 있습니다.
Assertion은 에러가 없는 프로그램을 작성하기 위한 하나의 수법을 의미합니다.
Node.js에서 지원하는 Assert 모듈을 사용할 수도 있으며, Chai나 Should.js 같은 BDD, TDD 방식의 다양한 Assertion 라이브러리가 있습니다.
Mocha는 각 모듈에서 별도로 호출하지 않으며, 사용할 Assertion 라이브러리만 require()
로 호출합니다.
전역으로 설치하면 mocha
명령어로 실행할 수 있습니다.
$ npm install mocha -g
개발 의존성 모듈로 설치할 경우 package.json
의 scripts
항목에 명령을 설정합니다.
$ npm install mocha --save-dev
package.json
:
{
"scripts": {
"test": "mocha"
}
}
Mocha에 대해서 살펴보기 전에, 아주 간단하게 테스트용 코드를 작성합니다.
app.js
:
module.exports = {
sayHello: function () {
return 'hello';
}
}
test/
디렉토리에 별도의 파일(app.spec.js
)을 생성한 후 테스트할 코드를 require()
로 가져와 테스트를 진행합니다.
test/app.spec.js
:
const sayHello = require('../app').sayHello;
if (sayHello) {
console.log('sayHello should return "hello"');
if (sayHello() === 'hello') {
console.log('Success');
} else {
console.log('Fail');
}
}
예전에는 위와 같이 Alert이나 Console로 작동 여부를 확인했지만, 우리는 Mocha를 활용합니다.
내용을 다음과 같이 수정하여 단위 테스트를 진행합니다.
describe()
는 테스트의 범위를 설정하고, it()
는 단위 테스트를 설정합니다.
인수로 사용한 done
은 비동기 단위 테스트를 완료할 때 유용합니다.
test/app.spec.js
:
const sayHello = require('../app').sayHello;
describe('App test!', function () {
it('sayHello should return hello', function (done) {
if (sayHello() === 'hello') {
done();
}
});
});
mocha <filename>
으로 실행할 경우 해당 파일의 테스트를 진행합니다.
만약 mocha
명령과 함께 실행 파일(filename)을 지정하지 않으면, 현재 경로의 test/
디렉토리에 있는 모든 .js
파일을 실행합니다.
우리는 test/
디렉토리 안에 app.spec.js
를 생성하였으므로 다음과 같이 실행할 수 있습니다.
$ mocha
# or
$ npm test
# or
$ mocha test/app.spec.js
App test!
✓ sayHello should return hello
1 passing (5ms)
Node.js에서 제공하는 assert
모듈을 활용하면 좀 더 편리하게 테스트할 수 있습니다.
const assert = require('assert'); // Node.js `assert` module
const sayHello = require('../app').sayHello;
describe('App test!', function () {
it('sayHello should return "hello"', function () {
assert.equal(sayHello(), 'hello');
});
});
Mocha를 이용하면 간단하게 비동기 테스트를 진행할 수 있습니다.it()
의 콜백 인수로 done
를 사용하면 자동으로 비동기 테스트를 인식하고, 비동기 로직이 완료 후 done()
을 실행하면 테스트가 완료됩니다.
const fs = require('fs');
describe('App test1', function () {
it('async test', function (done) {
fs.readFile(__filename, done);
});
});
비동기 테스트는 제한 시간(timeout)으로 2초가 경과하면 자동으로 테스트 실패가 됩니다.
테스트를 진행할 때 -t
, --timeout
옵션을 이용하거나, 단일 테스트 내 this.timeout()
을 이용하여 제한 시간을 설정할 수 있습니다.
# 전체 테스트의 제한 시간 설정
$ mocha -t 3000
혹은
const fs = require('fs');
describe('App test1', function () {
it('async test', function (done) {
this.timeout(3000); // 단일 테스트의 제한 시간 설정
fs.readFile(__filename, done);
});
});
describe()
안에 또 다른 describe()
를 중첩하여 사용할 수 있습니다.
app.js
:
module.exports = {
sayHello: function () {
return 'hello';
},
addNumbers: function (a, b) {
return a + b;
}
};
Assetion 라이브러리로 Chai의 Should 스타일을 사용했습니다.
test/app.spec.js
:
const should = require('chai').should();
const app = require('../app');
describe('# App test', function () {
describe('# sayHello', function () {
it('should return hello', function () {
app.sayHello().should.equal('hello');
});
it('should a string type', function () {
app.sayHello().should.be.a('string');
});
});
describe('# addNumbers', function () {
it('should greater than 5', function () {
app.addNumbers(3, 4).should.be.above(5);
});
});
});
$ npm test
# App test
# sayHello
✓ should return hello
✓ should a string type
# addNumbers
✓ should greater than 5
3 passing (9ms)
Mocha에서는 before()
, after()
, beforeEach()
, afterEach()
의 4가지 함수(hook)를 제공하여 테스트 코드 전후를 제어할 수 있습니다.
describe('hooks', function() {
before(function() {
// 블록 범위 내 모든 테스트 전에 실행
});
after(function() {
// 블록 범위 내 모든 테스트 후에 실행
});
beforeEach(function() {
// 블록 범위 내 각 테스트 직전에 실행
});
afterEach(function() {
// 블록 범위 내 각 테스트 직후에 실행
});
// test cases
});
const assert = require('assert');
const app = require('../app');
describe('App test!', function () {
before(function () {
console.log('before hook');
});
after(function () {
console.log('after hook');
});
beforeEach(function () {
console.log('beforeEach hook');
});
afterEach(function () {
console.log('afterEach hook');
});
it('A test', function () {
assert.equal(app.a(), 'A!');
});
it('B test', function () {
assert.equal(app.b(), 'B!');
});
});
$ npm test
App test!
before hook
beforeEach hook
✓ A test
afterEach hook
beforeEach hook
✓ B test
afterEach hook
after hook
2 passing (11ms)
mocha
명령어로 테스트를 진행할 때 다음과 같은 옵션을 사용할 수 있습니다.
$ mocha [debug] [options] [files]
-V
, --version
: Mocha의 버전을 확인합니다.-A
, --async-only
: 비동기 테스트만 허용합니다.-c
, --colors
: 코드 색상을 활성화합니다.-C
, --no-colors
: 코드 색상을 비활성화합니다.-R
, --reporter <name>
: 테스트 결과에 대한 리포팅 방식을 설정합니다.-b
, --bail
: 첫번째 실패한 테스트만 확인합니다.-g
, --grep <pattern>
: 지정한 정규표현식 패턴과 일치하는 테스트만 진행합니다.-f
, --fgrep <string>
: 지정한 문자열을 포함하는 테스트만 진행합니다.-i
, --invert
: --grep
, --fgrep
결과와 일치하지 않는 테스트만 진행합니다. ex> mocha -f 'abc' -i
-r
, --require <name>
: 가져올 모듈(should.js
, chai
같은 Assertion 라이브러리)을 지정합니다.-t
, --timeout <ms>
: 지정한 테스트가 제한 시간(기본값 2000
)을 지나면 실패합니다. 단일 테스트의 제한 시간을 설정할 수 있습니다.-u
, --ui <name>
: 사용자 인터페이스를 지정합니다. ex> mocha -u tdd
-w
, --watch
: 테스트 대상 파일들을 감시합니다.