이 글은 qiita에 올라온 フロントエンドにテストを導入이라는 아티클을 원 저자의 허락을 받고 번역한 글입니다.
오역이나 오타에 대한 지적은 환영합니다. 그림은 번역하지 않았습니다.

프론트엔드에 테스트를 도입

이 글은 스크린상 JavaScript 테스트를 해본 적이 없는 분 Mocha? webpack? karma? 각각을 다루는 글을 봤지만 전체적인 그림을 잘 모르겠는 분을 대상으로 합니다. (며칠 전의 나) 전체적인 도입 흐름을 설명하는 아티클이 있으면 전체적인 그림이 보기 쉬울 거 같다고 생각하여 작성합니다.

전제

Nodejs,npm,chrome이 설치되어 있어야 합니다.

흐름

Step 표제 목적
Step 0 테스트 대상 어플리케이션을 작성
Step 1 라이브러리를 사용하지 않고 테스트 브라우저의 console 기능을 이용한 테스트
Step 2 Mocha 도입 테스트 결과를 리치하게
Step 3 Nodejs로 테스트 실행 브라우저의 세계에서 Nodejs의 세계로 테스트를 가져가라, 후속 Step으로 이어짐
Step 4 webpack 도입 브라우저상에서 모듈을 읽어들일 수 있도록
Step 5 assert 도입 테스트 코드의 assertion 최적화
Step 6 karma 도입브라우저에서 테스트를 실행

링크는 소스로 이어집니다

목표

이후 스텝 마지막에도 넣어두었지만,
파란 배경이 실행관련 파일,
초록 배경이 테스트 관련 파일,
빨간 배경이 step 자체로 구분했습니다.

STEP 0 테스트 대상 어플리케이션 제작

텍스트 박스가 2개 있고, 계산 버튼을 누르면 더해서 보여주는 어플리케이션을 작성함

<body>
<div>
    <input type="number" id="value1"> +
    <input type="number" id="value2"> =
    <span id="result"></span><br>
    <input type="button" id="calc" value="계산"></input>
</div>
<script src="/src/calc-util.js"></script>
<script src="/src/index.js"></script>
</body>
document.getElementById('calc').addEventListener('click', function () {
    var value1 = Number(document.getElementById('value1').value);
    var value2 = Number(document.getElementById('value2').value);

    var result = add(value1, value2);
    document.getElementById('result').innerText = result;
});

아래가 테스트 코드입니다.

function add(value1, value2) {
    return value1 + value2;
}

STEP 1 라이브러리를 사용하지 않고 테스트

JS를 실행시키기 위한 HTML을 적당히 작성해서, 개발자 도구에서 로그를 보는 형태입니다.

<body>
<script src="/src/calc-util.js"></script>
<script src="/src/index.js"></script>
</body>
console.info('add 함수 테스트');
console.info('1+2는3이어야 한다');

if (add(1, 2) === 3) {
    console.info('성공')
} else {
    console.error('실패')
}

STEP 2 모카를 도입

이전 환경에서 일단 테스트를 작성해봤지만, 콘솔상 에러를 찾는게 꽤 힘듭니다.
그 문제를 해결하기 위해 MochaJasmine같은 테스트 프레임워크를 사용합니다.
이번에는 Mocha를 사용합니다.

테스트 코드를 수정

describe로 묶고 it으로 검증을 정의합니다.
성공케이스만 하면 재미없으니 실패 케이스에 대해서도 추가합니다.
※심플하게 하기 위해서 테스트 자체를 틀리게 했습니다 m(_ _)m

describe('add함수 테스트', function() {
    it('1+2는 3이다', function() {
        if (add(1, 2) === 3) {
        } else {
            throw new Error('실패');
        }
    });
    it('1+2는 4다', function() {
        if (add(1, 2) === 4) {
        } else {
            throw new Error('실패');
        }
    });
});

테스트 실행용 HTML 수정

CSS와 JS를 읽어들이는 부분과 세트업 수정, 출력결과 장소 등을 추가했습니다.

<head>
    <meta charset="UTF-8">
    <title>Step 2 spec</title>
    <link href="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css" rel="stylesheet" />
</head>
<body>
<div id="mocha"></div>

<script src="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js"></script>
<script>mocha.setup('bdd')</script>

<script src="/src/calc-util.js"></script>
<script src="/test/calc-util-spec.js"></script>

<script>mocha.run();</script>
</body>

실행결과

STEP3 Nodejs로 테스트 실행

수정을 할 때마다 브라우저에서 실행시키는 건 힘듭니다.
또 CI를 실행시키기 위해서도 브라우저를 사용하지 않고 Nodejs로 실행시킬 필요가 있습니다.
Mocha는 npm으로 설치하여 console에서 실행할 수 있습니다.

package.json을 작성

package.json(설정파일)을 만드는 것이라면 그냥 계속 OK만 누르면 됩니다

npm init

Mocha를 설치

npm i -D mocha

테스트 코드 수정

테스트 대상 코드를 읽어들이기 위해 require로 설정합니다.

var calcUtil = require('../src/calc-util.js');

describe('add 함수 테스트', function() {
    it('1+2는 3이다', function() {
        if (calcUtil.add(1, 2) === 3) {
        } else {
            throw new Error('실패');
        }
    });
    it('1+2는 4다', function() {
        if (calcUtil.add(1, 2) === 4) {
        } else {
            throw new Error('실패');
        }
    });
});

테스트 대상 코드를 수정

require로 읽어들이기 위해서 module.export로 묶습니다.
※실제로 동작하는 화면이 동작하지 않게 되지만 이 부분은 다음 step에서 해결됩니다. 일단 test를 합시다.

module.exports = {
    add : function (value1, value2) {
        return value1 + value2;
    }
}

package.json을 수정

아래와 같이 test를 mocha로 수정하고, npm test로 실행시킵니다.
※테스트 대상 기반이 test/*.js라서 Spec파일을 지정할 필요가 없습니다.

"scripts": {
    "test": "mocha",

테스트 실행

npm test

Step4 webpack 도입

require를 사용하기 위해 add 함수를 바꾼 탓에 실제 화면에서 동작하지 않습니다.
이를 해결하기 위해 webpack이나 browserify같은 툴을 사용합니다.
이번에는 webpack을 사용합니다.

webpack을 설치

npm i -D webpack

config 파일 작성

설정의 상세한 부분은 webpack入門(일본어)이라는 글에서 쉽게 설명하고 있습니다.
간단하게 작성하면 index.js와 require되어있는 calc-util.js를 app.bundle.js로 묶는 설정입니다.

touch webpack.config.js
module.exports = {
    entry: {
        app: './src/index.js'
    },
    output: {
        path: './dist',
        filename: '[name].bundle.js'
    }
};
        

package.json을 수정

아래와 같은 build를 추가해서, npm run build로 실행시킵니다.
※ webpack이라고만 해두면 기본으로 webpack.config.js를 사용합니다.

"scripts": {
    "test": "mocha",
    "build": "webpack",

add 함수를 부르는 js를 수정

테스트 코드와 같이 require를 사용해서 아래와 같이 수정합니다.

var calcUtil = require('./calc-util.js');

document.getElementById('calc').addEventListener('click', function () {
    var value1 = Number(document.getElementById('value1').value);
    var value2 = Number(document.getElementById('value2').value);

    var result = calcUtil.add(value1, value2);
    document.getElementById('result').innerText = result;
});

webpack을 실행

npm run build

이로써 app.bundle.js가 작성되어 브라우저에서 읽을 수 있게 됩니다.

불러들이는 HTML작성

원래 2개를 불러들이던 js가 1개로 줄어듭니다.

<body>
<div>
    <input type="number" id="value1"> + <input type="number" id="value2"> = <span id="result"></span><br>
    <input type="button" id="calc" value="計算"></input>
    </div>
<!--    <script src="/src/calc-util.js"></script> -->
<!--    <script src="/src/index.js"></script> -->
<script src="/dist/app.bundle.js"></script>
</body>

부가적으로 add 함수가 글로벌을 오염시키지 않는 메리트도 생깁니다.

Step 5 assert 도입

테스트를 좀 더 쉽게 해봅시다.
it이나 throw가 장황해보입니다. 이를 가능하게 하는 게 assert입니다.
Nodejs에 기본으로 탑재되어있는 라이브러리입니다
require('assert')로 불러들이고 assert()로 테스트 대상을 지정합니다.

var assert = require('assert');
var calcUtil = require('../src/calc-util.js');

describe('add 함수 테스트', function() {
    it('1+2는 3이다', function() {
        assert(calcUtil.add(1, 2) === 3);
    });
    it('1+2는 4다', function() {
        assert(calcUtil.add(1, 2) === 4);
    });
});

꽤 깔끔해졌습니다.

Step 6 karma 도입

지금까지는 Nodejs의 세계에서 테스트를 했습니다.
그러나 브라우저에 따라 동작하지 않는 함수를 사용하면 에러가 될 수도 있습니다.
이걸 해결하기 위해 karma라고 하는 브라우저상에서 동작하는 라이브러리를 사용합니다.

karma 관련 모듈 설치

npm i -D karma karma-chrome-launcher karma-mocha karma-webpack

karma가 본체입니다. 이외에는 플러그인이며,
karma-chrome-launcher는 chrome실행용
karma-mocha가 mocha용
karma-webpack이 webpack용입니다.

2016-05-08 추가

karma-cli 글로벌 설치를 필요로 합니다.

npm i -g karma-cli

초기설정파일을 작성

커맨드를 입력하여 질문에 답하는 걸로 간단한 설정파일(karma.config.js)을 만들 수 있습니다.

karma init

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> mocha

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no

Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
>

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> test/**/*spec.js
18 04 2016 20:26:02.739:WARN [init]: There is no file matching this pattern.

>

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
>

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes

Require.js는 필요한 듯 보이지만 실제로는 필요없습니다. ※karma-webpack에서 require를 변환한 파일을 만들기 때문입니다.

karma.config.js 수정

위에 작성한걸 후술한 karma start로 실행시키면 Uncaught ReferenceError: require is not defined라 나옵니다.
브라우저 상에서 동작시키기 때문에 require를 찾지 못하기 때문입니다.
Step 4에서 webpack을 통해 require를 변환시켰을 때와 같은 스텝이 필요합니다.
karma에서는 preprocessors 설정으로 webpack을 통하도록 설정할 수 있습니다.

preprocessors: {
},
preprocessors: {
    'test/**/**spec.js': ['webpack']
},

package.json 수정

npm test로 실행하기 위한 설정이 필요합니다.

"test": "mocha",
"test": "karma start",

karma 구동

npm test

크롬이 실행된 뒤 터미널 상에 테스트 결과가 표시됩니다.
설정 파일을 만들었을 때 마지막 질문을 true라고 했기 때문에 테스트 파일이나 테스트 대상 파일을 수정할 때마다, 자동으로 테스트합니다.
테스트나 소스를 수정한 뒤 브라우저 화면을 F5로 갱신하여 확인하는 수고도 덜어집니다.

여담

그림은 draw.io에서 1파일 안에서 모두 만들고 png 엑스포트해서 각각 범위 선택 캡쳐했습니다.
더 좋은 방법이 있다면 알려주세요.m(_ _)m