Karma 테스트 러너로 ES6+ 의 코드를 유닛 테스트하기

Explanation

Karma 테스트 러너로 ES6+ 의 코드를 유닛테스트 하는 방법에 대해 적어보려 합니다. 이 글에서는 추가로 Webpack과 Babel을 사용하고 테스트 프레임워크로 Jasmine을 사용하여 간단한 테스트 코드를 작성하고 Karma가 정상적으로 동작하는지 확인합니다.

샘플코드 : https://github.com/falsy/blog-post-example/tree/master/karma-webpack-babel-quickstart

1. npm init

$ npm init

우선 init을 통해 기본적인 패키지 설정을 입력합니다.

2. 모듈 설치

2-1. Webpack

$ npm install --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader

'webpack'은 그 'Webpack', 'webpack-cli' 커맨드 명령어를 편하게 해주는?(cli: Command Line Interface), 'webpack-dev-server'는 로컬에 서버를 열어주는?, 'html-webpack-plugin'은 Webpack으로 만들어진 번들 파일을 html 파일에 넣어주는(또는 가상의 index.html을 만들어주는) Webpack 플러그인?, 'babel-loader'는 Babel을 Webpack에서 사용할 수 있도록 불러오는?? 모듈입니다.
(이건.. 설명을 한것도 아니고 안한것도 아니네요..)

2-2. Babel

$ npm install --save-dev babel-core babel-preset-env

'babel-core'는 그 'babel', 'babel-preset-env'는... 이전에 'babel-preset-es2015', 'babel-preset-es2016', 'babel-preset-es2017', 'babel-preset-latest' 같이 많이 사용되던 프리셋을 대체하는 프리셋 입니다. 대충.. ES6, ES7, ES8 -> ES5로 트랜스파일 해주는 프리셋??

2-3. Karma

$ npm install --save-dev karma karma-chrome-launcher jasmine-core karma-jasmine karma-webpack

'karma'은 그 'karma', 'karma-chrome-launcher'는 크롬 브라우저로 테스트하기 위한??, 'jasmine-core'는 그 BDD 테스트 프레임웍 Jasmin, 'karma-jasmine' 카르마와 재스민을 이어주고, 'karma-webpack'을 카르마와 웹팩을 이어줍니다.

3. 디렉토리 구조

/src
  index.html
  index.js
/test
  testSpec.js
karma.conf.js
package.json
webpack.config.js

이 글은 위와 같은 디렉토리 구조로 만들것 입니다.

4. webpack.config.js 작성

const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const path = require('path');

module.exports = {
  // entry: './src/index.js', 시작점? 작성하지 않으면 기본값이 './src/index.js'입니다.
  output: {
    path: path.resolve(__dirname, 'dist'), //번들을 만들 위치?
    filename: 'bundle.js' //번들파일이름
  },
  mode: 'development',
  module: {
    rules: [{
      test: /\.js?$/, // '.js'로 끝난 파일들이 대상
      loader: 'babel-loader', // 아까 설치한 바벨 모듈
      options: {
        presets: ['env'] // 아까 설치한 프리셋('babel-preset-env')
      },
      exclude: ['/node_modules'] // 제외할 대상
    }],
  },
  plugins: [
    // 아까 설치한 플러그인('html-webpack-plugin') './src/index.js'에 번들파일을 인클루드 시켜줍니다.
    // new HtmlWebpackPlugin() : template 값을 입력하지 않으면 가상의 기본 index.html을 만들어 줍니다.
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

5. karma.conf.js 작성

module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine'], // jasmine 프레임워크를 사용. 아까 설치한('jasmine-core', 'karma-jasmine')

    // 여기부터 기본 init에서 수정 사항
    // 웹팩 설정 가져오기?? 아까 설치한 모듈('karma-webpack')
    webpack: {
        mode: 'development',
        module: {
            rules: [{
              test: /\.js?$/,
              loader: 'babel-loader',
              options: {
                presets: ['env']
              },
              exclude: ['/node_modules']
            }]
        }
    },
    // 테스트할 대상 파일
    files: [
        // test 디렉토리에 Spec.js로 끝나는 파일
        // watched - 스펙 코드의 변화를 감지
        { pattern: 'test/*Spec.js', watched: true }
    ],
    preprocessors: {
        'test/*Spec.js': ['webpack'], // 'test/*Spec.js' 을 실행하기 전에 'webpack'을 선행??
    },
    // 여기까지 기존 init 이후 수정한 부분입니다.

    exclude: [
    ],
    reporters: ['progress'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
    concurrency: Infinity
  })
}

위 코드의 주석으로 표시한 부분을 제외하면 대부분 init 기본값입니다.
(직접 아래의 명령어로 init 후 위 코드의 주석된 부분을 추가하셔도 됩니다.)

// 'karma-cli'를 설치했다면
$ karma init
// 설치하지 않았다면
$ ./node_modules/karma/bin/karma init

6. package.json 수정

{
  "name": "karma-webpack-babel-quickstart",
  "version": "0.1.0",
  "description": "karma webpack babel quickstart",
  "main": "index.js",
  "scripts": {
    "start": "webpack-dev-server", // 커맨드 명령어 'npm start'를 입력하면 개발 서버를 실행
    "test": "./node_modules/karma/bin/karma start" // 커맨드 명령어 'npm test'를 입력하면 카르마 테스트러너 실행
  },
  "repository": {
    "type": "git",
    "url": "git+ssh://git@github.com:/falsy/karma-webpack-babel-quickstart.git"
  },
  "author": "falsy",
  "license": "WTFPL",
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.4",
    "babel-preset-env": "^1.6.1",
    "html-webpack-plugin": "^3.2.0",
    "jasmine-core": "^3.1.0",
    "karma": "^2.0.0",
    "karma-chrome-launcher": "^2.2.0",
    "karma-jasmine": "^1.1.1",
    "karma-webpack": "^3.0.0",
    "webpack": "^4.5.0",
    "webpack-cli": "^2.0.14",
    "webpack-dev-server": "^3.1.3"
  }
}

7. index.html 작성

<!-- src/index.html -->
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <title>falsy quick start</title>
</head>
<body>
  hello world
</body>
</html>

8. index.js 작성

// src/index.js
class TestClass {

  constructor() {
    this.resetTestValue();
  }

  resetTestValue() {
    this.testValue = 0;
  }

  setTestValue(value) {
    this.testValue = value;
  }

}

export default new TestClass()

9. TestSpec.js 작성

// test/TestSpec.js
import TestClass from '../src/index'; // srs/index.js 를 불러와서

describe('Test 1', function() {

  // it(..)이 실행될때마다 이전에 실행
  beforeEach(function() {
    // Reset
    TestClass.resetTestValue();
  });

  it('Init Value', function() {
    //TestClass.testValue 기본값은 'TestClass.resetTestValue()'이 실행된 이후기 때문에 0
    expect(TestClass.testValue).toEqual(0);
    // TestClass.testValue 가 0이면 success
  });

  it('Set Test Value', function() {
    //TestClass.testValue 기본값은 'TestClass.resetTestValue()'이 실행된 이후기 때문에 0
    const newValue = 2;
    TestClass.setTestValue(newValue);
    // 'TestClass.testValue = 2' 가 됐을거에요
    expect(TestClass.testValue).toEqual(newValue);
    // TestClass.testValue 가 2 가 됐으면 success
  });

});

10. 끝

// 개발 서버 실행
$ npm start
// 카르마 테스트 러너 실행
$ npm test

부족한 설명과.. 많은 오타를 포함하고 있을 수 있습니다..
잘못되거나 부족한 부분은 댓글을 통해 알려주시면 감사하겠습니다 :)