[Node.js – 기초 강좌] 9. Test 코드 작성하기(with Mocha, Chai)

Node.js에서 테스트 코드를 작성하는 것은 코드의 정확성, 안정성, 유지보수성을 보장하는 데 중요한 역할을 합니다. 이를 통해 개발자는 코드가 예상대로 동작하는지 확인하고, 코드 변경이 기존 기능에 영향을 미치지 않도록 할 수 있습니다. 테스트는 소프트웨어 개발의 필수적인 부분이며, 다양한 테스트 프레임워크와 라이브러리를 사용하여 효율적으로 작성할 수 있습니다. 이번 포스팅에서는 Mocha와 Chai와 supertest를 이용한 테스트 코드를 작성하는 방법을 알아보겠습니다.

Node.js에서 테스트(Test)란?

Node.js에서 테스트는 작성된 코드가 예상대로 작동하는지 검증하기 위해 수행하는 프로세스입니다.

테스트는 코드의 품질을 보장하고, 코드 변경이 기존 기능에 부정적인 영향을 미치지 않도록 합니다.

Node.js에서는 다양한 테스트 프레임워크와 라이브러리를 사용하여 테스트를 작성하고 실행할 수 있습니다.

Test in node.js

1. 테스트의 중요성

  • 버그 발견: 코드 작성 초기 단계에서 버그를 발견하여 수정할 수 있습니다.
  • 코드 품질 향상: 테스트를 통해 코드의 안정성과 신뢰성을 높일 수 있습니다.
  • 자동화: 지속적인 통합(CI, Continuous Integration) 환경에서 테스트를 자동으로 실행하여 코드 변경이 문제를 일으키지 않는지 확인할 수 있습니다.
  • 문서화: 테스트는 코드의 기능을 설명하는 데 도움이 되며, 다른 개발자들이 코드를 이해하는 데 유용합니다.

2. 테스트의 종류

Node.js에서 일반적으로 사용되는 테스트의 종류는 다음과 같습니다:

  • 단위 테스트(Unit Test): 개별 함수나 모듈을 테스트하여 예상되는 결과를 확인합니다.
  • 통합 테스트(Integration Test): 여러 모듈이 함께 작동하는지 확인합니다.
  • 엔드 투 엔드 테스트(End-to-End Test): 애플리케이션의 전체 흐름을 테스트하여 사용자 시나리오를 검증합니다.
  • 성능 테스트(Performance Test): 애플리케이션의 성능과 반응 속도를 테스트합니다.
  • 회귀 테스트(Regression Test): 새로운 코드 변경이 기존 기능에 영향을 미치지 않는지 확인합니다.

Test 모듈

Node.js 프로젝트에서 사용할 수 있는 다양한 테스트 모듈들이 있으며, 각 모듈은 고유한 특징과 장점을 가지고 있습니다. 주요 테스트 모듈과 그 특징을 살펴보겠습니다.

그리고 소개해준 아래 모듈들 중 이번 포스팅에서는 Mocha와 Chai를 사용해볼 예정입니다.

1. Mocha

Mocha는 Node.js 및 브라우저를 위한 기능이 풍부한 테스트 프레임워크입니다.

2. Jest

Jest는 Facebook에서 개발한 JavaScript 테스트 프레임워크로, 특히 React 프로젝트에서 널리 사용됩니다.

  • 특징:
    • 기본적으로 내장된 assertion 라이브러리와 모의(Mock) 기능
    • 코드 커버리지 보고서 자동 생성
    • 스냅샷 테스트 지원 (UI 테스트에 유용)
    • 빠르고 병렬로 테스트 실행
    • 설정이 간단하고 바로 사용 가능

3. Chai

Chai는 BDD/TDD 스타일의 assertion 라이브러리로, Mocha와 같은 테스트 프레임워크와 함께 사용됩니다.

  • 특징:
    • 세 가지 주요 assertion 스타일 지원: Should, Expect, Assert
    • 플러그인 확장 가능
    • 다양한 내장 assertion 메서드 제공

4. Jasmine

Jasmine은 행동 기반 테스트 프레임워크로, JavaScript 테스트를 위한 가장 오래된 프레임워크 중 하나입니다.

  • 특징:
    • 종속성 없이 독립적으로 사용 가능
    • BDD 스타일의 테스트 작성 가능
    • 내장된 스파이(Spy)와 스텁(Stub) 기능
    • 브라우저와 Node.js 환경 모두에서 사용 가능

5. Ava

Ava는 최신 JavaScript 기능을 활용한 테스트 프레임워크로, 간결하고 빠른 테스트 실행을 목표로 합니다.

  • 특징:
    • 테스트의 병렬 실행을 기본으로 하여 빠른 테스트 속도 제공
    • 간단하고 간결한 문법
    • 비동기 테스트 지원을 위한 Promises와 async/await 사용
    • 기본적으로 설치되는 코드 커버리지 지원

Mocha 소개

Mocha란?

Mocha는 Node.js 및 브라우저 환경에서 사용 가능한 기능이 풍부한 자바스크립트 테스트 프레임워크입니다.

테스트 작성과 실행에 필요한 다양한 기능을 제공하여, 개발자가 코드의 품질을 보장할 수 있도록 돕습니다.

Mocha 공식 페이지 (with Document)

Mocha repository

주요 특징

  1. 비동기 테스트 지원:
    • Mocha는 비동기 코드 테스트를 쉽게 작성할 수 있도록 도와줍니다. 콜백, 프로미스, async/await을 모두 지원합니다.
  2. BDD와 TDD 스타일 지원:
    • Mocha는 BDD(Behavior Driven Development)와 TDD(Test Driven Development) 스타일의 테스트 작성 방식을 모두 지원합니다. describe, context, it 등의 BDD 스타일과 suite, test 등의 TDD 스타일을 사용할 수 있습니다.
  3. 다양한 리포터:
    • Mocha는 테스트 결과를 다양한 형식으로 출력할 수 있는 리포터를 제공합니다. 기본적으로 콘솔에서 읽기 쉽게 출력하는 ‘spec’ 리포터 외에도, JSON, HTML 등의 형식을 지원하는 여러 리포터가 있습니다.
  4. 확장성:
    • Mocha는 플러그인과 미들웨어를 통해 확장할 수 있습니다. 다양한 assertion 라이브러리와의 통합이 용이하며, 필요에 따라 커스텀 리포터나 미들웨어를 작성할 수 있습니다.
  5. 설정 파일을 통한 구성:
    • Mocha는 .mocharc.json, .mocharc.yml 등 다양한 형식의 설정 파일을 통해 동작 방식을 쉽게 구성할 수 있습니다.

주요 Class 소개

1. Mocha

Mocha 클래스는 Mocha의 핵심 클래스입니다.

이 클래스는 Mocha 인스턴스를 생성하고, 테스트 Suite와 테스트 케이스를 관리하며, 테스트를 실행하는 데 사용됩니다.

예)

const Mocha = require('mocha');
const mocha = new Mocha();
mocha.addFile('test/test.js');
mocha.run();
2. Runner

Runner 클래스는 테스트 실행을 관리합니다.

테스트 케이스를 순차적으로 실행하고, 각 테스트의 결과를 처리합니다.

Runner는 내부적으로 사용되며, 테스트 결과를 리포터에 전달합니다.

run()을 통해 Test를 실행하며, on(event, listener)를 통해 이벤트 리스너를 등록합니다. 이벤트로는 pass, fail, end 가 있습니다.

const runner = mocha.run();
runner.on('pass', (test) => {
  console.log('Test passed:', test.fullTitle());
});
runner.on('fail', (test, err) => {
  console.log('Test failed:', test.fullTitle(), err);
});
3. Suite

Suite 클래스는 테스트 스위트를 나타냅니다.

Suite는 테스트 케이스를 그룹화하고, 각 스위트의 설정과 클린업 작업을 정의할 수 있습니다.

주요 Method 및 Property:

  • addTest(test): 테스트 케이스를 추가합니다.
  • beforeAll(fn): 모든 테스트 케이스가 실행되기 전에 한 번 실행할 함수를 정의합니다.
  • afterAll(fn): 모든 테스트 케이스가 실행된 후에 한 번 실행할 함수를 정의합니다.
  • beforeEach(fn): 각 테스트 케이스가 실행되기 전에 실행할 함수를 정의합니다.
  • afterEach(fn): 각 테스트 케이스가 실행된 후에 실행할 함수를 정의합니다.

예)

const suite = Mocha.Suite.create(mocha.suite, 'My Suite');
suite.beforeAll(() => {
  console.log('Before all tests');
});
suite.afterAll(() => {
  console.log('After all tests');
});
suite.beforeEach(() => {
  console.log('Before each test');
});
suite.afterEach(() => {
  console.log('After each test');
});
4. Test

Test 클래스는 개별 테스트 케이스를 나타냅니다.

각 테스트 케이스는 it 함수로 정의되며, 테스트 실행 로직과 예상 결과를 포함합니다.

주요 메서드 및 프로퍼티:

  • title: 테스트 케이스의 제목을 나타냅니다.
  • fn: 테스트 케이스로 실행할 함수입니다.
  • run(fn): 테스트 케이스를 실행합니다.
const test = new Mocha.Test('should return true', function() {
  assert.equal(true, true);
});
suite.addTest(test);
5. Context

Context 클래스는 테스트 실행 시 컨텍스트를 제공합니다.

this 키워드를 통해 접근할 수 있으며, 각 테스트 케이스나 훅에서 공유 데이터를 저장하는 데 사용할 수 있습니다.

describe('Test Suite', function() {
  beforeEach(function() {
    this.someValue = 42;
  });

  it('should have someValue set to 42', function() {
    assert.equal(this.someValue, 42);
  });
});
6. Hook

Hook 클래스는 각 테스트 스위트의 설정과 클린업 작업을 정의하는 훅을 나타냅니다.

before, after, beforeEach, afterEach와 같은 훅이 있습니다.

const hook = new Mocha.Hook('beforeEach hook', function() {
  console.log('Running before each test');
});
suite.addHook(hook);

Chai 소개

Chai란?

Chai는 BDD/TDD 스타일의 assertion 라이브러리입니다.

Mocha와 같은 테스트 프레임워크와 함께 사용되어, 테스트 코드에서 예상 결과를 검증하는 데 사용됩니다.

주요 특징

  1. 다양한 assertion 스타일:
    • Chai는 세 가지 주요 assertion 스타일을 지원합니다
      : Should, Expect, Assert. 이를 통해 다양한 방식으로 테스트 결과를 검증할 수 있습니다.
  2. 확장성:
    • Chai는 플러그인을 통해 기능을 확장할 수 있습니다.
    • Chai-as-promised와 같은 플러그인을 사용하면 비동기 코드 테스트를 더욱 쉽게 작성할 수 있습니다.
  3. 유연성:
    • Chai는 다양한 테스트 시나리오에 맞게 유연하게 사용할 수 있습니다.
    • BDD와 TDD 스타일을 모두 지원하여 개발자의 선호에 따라 선택할 수 있습니다.

주요 Class 소개

1. expect 스타일

expect 스타일은 함수형 assertion 스타일로, 간결하고 읽기 쉬운 문법을 제공합니다.

주요 메서드

  • to.equal(value): 기대한 값과 실제 값이 일치하는지 확인합니다.
  • to.be.a(type): 값의 타입을 확인합니다.
  • to.have.property(name, [value]): 객체가 특정 속성을 가지고 있는지, 그리고 그 값이 일치하는지 확인합니다.
  • to.include(value): 배열이나 문자열에 특정 값이 포함되어 있는지 확인합니다.
  • to.be.true / to.be.false: 값이 참인지 또는 거짓인지 확인합니다.
  • to.be.null / to.be.undefined: 값이 null 또는 undefined인지 확인합니다.
const { expect } = require('chai');
expect(2 + 2).to.equal(4);
expect('foo').to.be.a('string');
expect([1, 2, 3]).to.include(2);
expect({ foo: 'bar' }).to.have.property('foo');
expect(true).to.be.true;
expect(null).to.be.null;
2. should 스타일

should 스타일은 BDD 스타일의 assertion을 제공합니다.

객체의 프로토타입을 확장하여 자연어와 같은 문법을 사용합니다.

주요 메서드

  • should.equal(value): 기대한 값과 실제 값이 일치하는지 확인합니다.
  • should.be.a(type): 값의 타입을 확인합니다.
  • should.have.property(name, [value]): 객체가 특정 속성을 가지고 있는지, 그리고 그 값이 일치하는지 확인합니다.
  • should.include(value): 배열이나 문자열에 특정 값이 포함되어 있는지 확인합니다.
  • should.be.true / should.be.false: 값이 참인지 또는 거짓인지 확인합니다.
  • should.be.null / should.be.undefined: 값이 null 또는 undefined인지 확인합니다.
const chai = require('chai');
const should = chai.should();
(2 + 2).should.equal(4);
'foo'.should.be.a('string');
[1, 2, 3].should.include(2);
({ foo: 'bar' }).should.have.property('foo');
true.should.be.true;
null.should.be.null;
3. assert 스타일

assert 스타일은 TDD 스타일의 assertion을 제공합니다.

함수 기반 assertion 을 사용하여 간결하게 assertion 을 작성할 수 있습니다.

주요 메서드

  • assert.equal(actual, expected): 실제 값과 기대한 값이 일치하는지 확인합니다.
  • assert.typeOf(value, type): 값의 타입을 확인합니다.
  • assert.property(object, property): 객체가 특정 속성을 가지고 있는지 확인합니다.
  • assert.include(array, value): 배열이나 문자열에 특정 값이 포함되어 있는지 확인합니다.
  • assert.isTrue(value): 값이 참인지 확인합니다.
  • assert.isFalse(value): 값이 거짓인지 확인합니다.
  • assert.isNull(value): 값이 null인지 확인합니다.
  • assert.isUndefined(value): 값이 undefined인지 확인합니다.
const assert = require('chai').assert;
assert.equal(2 + 2, 4);
assert.typeOf('foo', 'string');
assert.include([1, 2, 3], 2);
assert.property({ foo: 'bar' }, 'foo');
assert.isTrue(true);
assert.isNull(null);

supertest

소개

supertest는 HTTP assertions을 위한 라이브러리로, Node.js 환경에서 Express와 같은 웹 애플리케이션을 테스트하는 데 널리 사용됩니다.

supertest를 사용하면 HTTP 요청을 시뮬레이션하고 응답을 검증할 수 있습니다.

주요 기능

  • HTTP 요청 시뮬레이션: GET, POST, PUT, DELETE 등 다양한 HTTP 요청을 보낼 수 있습니다.
  • 응답 검증: 상태 코드, 헤더, 본문 등을 검증할 수 있습니다.
  • Promise 기반: 비동기 테스트를 쉽게 작성할 수 있도록 Promise를 지원합니다.

기본 사용법

설치

$ npm install supertest --save-dev

주요 Class 및 method

1. request(app)

테스트할 애플리케이션을 설정합니다. app은 Express 애플리케이션 인스턴스입니다.

import request from "supertest";
import app from "../src/app";

request(app)
  .get('/')
  .expect(200);
2. HTTP 메소드

supertest는 HTTP 메소드(GET, POST, PUT, DELETE 등)에 해당하는 메소드를 제공합니다.

각 메소드는 URL 경로를 인수로 받아 해당 경로에 요청을 보냅니다.

request(app)
  .get('/path')
  .expect(200); // GET 요청

request(app)
  .post('/path')
  .send({ key: 'value' })
  .expect(201); // POST 요청

request(app)
  .put('/path')
  .send({ key: 'value' })
  .expect(200); // PUT 요청

request(app)
  .delete('/path')
  .expect(204); // DELETE 요청
3. .expect(status[, body])

응답의 상태 코드와 선택적으로 본문을 검증합니다.

상태 코드만 검증할 때는 첫 번째 인수로 상태 코드를 전달하고, 본문을 함께 검증할 때는 두 번째 인수로 본문을 전달합니다.

request(app)
  .get('/path')
  .expect(200);

request(app)
  .post('/path')
  .send({ key: 'value' })
  .expect(201, { message: 'Created', key: 'value' });
4. .send(data)

POST나 PUT 요청의 본문 데이터를 보낼 때 사용합니다.

data는 JSON 객체일 수 있습니다.

request(app)
  .post('/path')
  .send({ key: 'value' })
  .expect(201);
5. .set(field, value)

요청 헤더를 설정합니다. field는 헤더 이름, value는 헤더 값입니다.

request(app)
  .get('/path')
  .set('Authorization', 'Bearer token')
  .expect(200);
6. .query(params)

쿼리 문자열을 설정합니다. params는 쿼리 문자열에 추가할 키-값 쌍의 객체입니다.

request(app)
  .get('/search')
  .query({ q: 'term' })
  .expect(200);
7. .end(callback)

요청을 끝내고 콜백 함수를 실행합니다. 콜백 함수는 errres 인수를 받습니다.

이를 통해 요청이 완료된 후 추가적인 검증을 수행할 수 있습니다.

request(app)
  .get('/path')
  .expect(200)
  .end((err, res) => {
    if (err) throw err;
    console.log(res.body);
  });
8. .auth(user, pass)

기본 인증을 설정합니다. user는 사용자 이름, pass는 비밀번호입니다.

request(app)
  .get('/path')
  .auth('username', 'password')
  .expect(200);
nodejs-mocha-chai-supertest

전체 예제 프로젝트

아래 git hub repository에 전체 예제 코드를 업로드해 놓았습니다.

devitworld nodejs tutorial repository

Leave a Comment