Karma + enzyme를 활용한 React 컴포넌트 테스트하기

December 05, 2017

요즘들어 React project를 같이 참여해서 하고 있으면서, 여러가지 분야에 관심이 가는 경우들이 있습니다.
특히, TDD, BDD등 테스트 주도 개발 및 함수형 프로그래밍에 관심을 계속 두고 있는편인데, React 개발에 있어서 컴포넌트 또는 Redux를 활용한 container 테스트는 매우 중요해 보였습니다.
이번 포스팅은 React component를 테스트하기 위해 적합한 테스트 자동화 도구 Karma와 airbnb에서 만든 React 컴포넌트 테스트 도구 enzyme의 설치 및 사용 방법에 대한 내용입니다.

테스트 자동화 도구 - Karma

karma는 front-end 테스트 자동화 도구로써, Chrome, Firefox 및 Phantom JS 브라우저 플랫폼을 사용할 수 있으며, Mocha, Jasmine 등의 기타 유효성 플러그인과 연동하여 사용할 수 있는 테스트 러너입니다. 또한, gulp, webpack과의 연동으로 bundling된 파일을 테스트 해볼 수 있으므로, 현재 front-end 테스트 러너 중에서는 연동 플러그인도 많고 쓸만한 플러그인이라 생각됩니다.

React Component 테스트 플러그인 - enzyme

enzyme는 그 유명한 airbnb에서 개발한 React용 컴포넌트 테스트 플러그인입니다. component를 얕게(shallow) 렌더링하여 테스트 해볼 수도 있으며, full DOM을 로딩하여 확인할 수도 있습니다. 또한, 개발자들에게 친숙한 jquery형태로 component를 조회할 수 있기 때문에, 컴포넌트용 테스트로는 enzyme가 제일 막강해 보이더군요.

사용하기

이번 포스팅에는 karma와 mocha, chai를 이용할 예정이며, React는 버전 15를 사용할 예정입니다. 런처(브라우저)는 Chrome을 사용하며, webpack을 같이 사용할 예정입니다.

1. karma 설치하기

karma 및 enzyme를 이용하기 위해서는 node.js 및 npm이 설치되어 있어야 합니다. node.js, npm이 설치된 이후, React 프로젝트 경로의 terminal에서, 다음과 같이 command를 입력합니다.

npm install karma karma-cli karma-mocha karma-chrome-launcher karma-verbose-reporter karma-sourcemap-loader karma-webpack mocha chai --save-dev

karma-verbose-reporter는 command 창 내에 테스트 결과 및 describe에 대한 부분을 리스팅해서 보여줄 수 있는 플러그인입니다.

2. enzyme, babel-preset-airbnb 설치

enzyme를 사용하기 위해서는 babel에서 airbnb preset을 설치하셔야 합니다.

npm install enzyme babel-preset-airbnb

3. React 버전이 16 미만인 경우

enzyme의 경우 React 상위 버전으로 호환이 되기 때문에, 이전 버전인 경우 adapter를 설치해 주어야 합니다. 16버전 미만인 경우(여기서는 15버전을 예로 들겠습니다.) enzyme-adpater를 npm에서 설치합니다. 또한, react-test-renderer도 사용하시는 react 버전에 맞게 설치합니다.

# react version <= 16
# npm install enzyme-adapter-react-"version" react-test-renderer@"version" --save-dev
npm install enzyme-adapter-react-15 react-test-renderer@15 --save-dev

4. karma.conf.js 작성

karma 설정을 위한 js 파일을 작성할 차례입니다. 프로젝트 상위에 karma.conf.js를 다음과 같이 작성합니다.

const webpack = require('webpack');
const path = require('path');

module.exports = function (config) {
	config.set({
		browsers: ['Chrome'],

    singleRun: true,

    frameworks: ['mocha'],

    files: [
		// webpack test 설정을 작성할 file
      'webpack.test.config.js'
    ],

    preprocessors: {
      'webpack.test.config.js': ['webpack', 'sourcemap']
    },

    reporters: ['verbose', 'dots'],

    webpack: {
      devtool: 'inline-source-map',
      module: {
        rules: [
          {
            test: /\.scss$/,
            use: [
              'style-loader',
              'css-loader',
              'sass-loader'
            ]
          },
          {
            test: /\.(png|svg|jpg|gif)$/,
            use: [
              'file-loader'
            ]
          },
          {
            test: /\.(woff|woff2|eot|ttf|otf)$/,
            use: [
              'file-loader'
            ]
          },
          {
            test: /\.js$/,
            exclude: /(node_modules)/,
            use: [
              {
                loader: 'babel-loader',
                options: {
                  presets: ['env', 'react', 'stage-2', 'airbnb']
                }
              }
            ]
          }
        ]
      }
    },

    webpackServer: {
      noInfo: true
    }
	});
};

설정 파일을 살펴보면, browser에는 Chrome, frameworks에는 mocha를 사용했습니다. files에는 webpack test를 작성할 file을 추가합니다. (여기서는 webpack.test.config.js라는 파일로 추가할 예정입니다.) preprocesor에는 해당 테스트를 실행하기 전의 task를 정의하는데, 여기서는 webpack과 sourcemap을 사용할 예정입니다. 테스트 결과를 보고할 tool은 verbose 및 dots를 사용하며, webpack에는 React에서 사용한 webpack config를 그대로 사용합니다.

5. webpack.test.config.js

webpack을 통해 test할 목록들을 작성합니다. 또한, 최 상단에 enzyme adapter에 대한 부분을 설정함으로써 16버전 이하의 React 프로젝트에도 enzyme를 사용할 수 있도록 해줍니다.

// react version이 16보다 낮은경우
const enzyme = require('enzyme');
const Adapter = require('enzyme-adapter-react-15');

enzyme.configure({ adapter: new Adapter() });

// 필수 작성
const context = require.context('./src', true, /-test\.js$/);
context.keys().forEach(context);

위의 소스에서 보시면 작업한 프로젝트 내에 src에 있는 폴더 내에서 -test.js라고 되어 있는 파일만 골라서 test를 진행하도록 설정되어 있습니다.

6. component 작성

테스트할 component를 src폴더 내에 생성해 봅니다.

// src/Example.js
import React, { Component } from 'react';

class Example extends Component {
	constructor(props) {
		super(props);
	}
	
	render() {
		return (
			<div>
				<div className="test-container"></div>
			</div>
		);
	}
}

export default Example;

7. src 폴더 내에 Example-test.js 작성

이제 테스트 파일을 작성할 차례입니다. src 폴더 내에 Example Component 테스트를 위한 Example-test.js를 작성합니다.

// src/Example-test.js
import React from 'react';
import { expect } from 'chai';
import { shallow } from 'enzyme';
import Example from './Example';

describe('<Example />', () => {
	it('Example rendering component without problem', () => {
		const component = shallow(<Example />);
		expect(component).to.exist;
	});

	it('Example inner child test-container', () => {
		const component = shallow(<Example />);
		expect(component.find('.test-container').exists()).to.equal(true);
	});
});

위의 코드는 karma에서 기본적인 코드로써, describe를 통해 대 분류를, it을 통해서 각 성능 테스트별 설명을 첫번째 인자값으로, 두번째 인자값으로 function 내에 테스트할 코드를 작성합니다. enzyme를 활용해서 Example 컴포넌트를 import 해와서 shallow 렌더링을 진행했고, 첫번째 테스트는 이 컴포넌트가 오류 없이 렌더링 하는지를 테스트합니다.

두번째 it의 경우에는 test-container라는 클래스를 가진 child 객체를 찾는 것입니다. enzyme는 jQuery와 유사하게 find 함수를 통해서 class, id를 찾아올 수 있습니다. 두번째 코드의 경우 test-container 클래스가 있는 컴포넌트가 존재하는지를 테스트하는 코드입니다.

8. 테스팅

모든 환경설정이 끝났다면, 테스트 하는 방법은 간단합니다. command 창에 다음과 같이 입력합니다.

karma start

해당 command를 실행하면 terminal에 각 describe, it에 대한 설명과 테스트가 성공적으로 통과했는지, 실패했는지가 표시됩니다. 위와 같은 과정을 통해서 모든 component에 대한 테스트가 가능합니다.

...