[Jest] Testing for node.js

Suyeon·2021년 11월 21일
0

Javascript

목록 보기
31/31
post-thumbnail

💡 Jest로 node.js 어플리케이션 테스트하기

1. Install

  • 파일의 확장자는 module_name.test.js로 작성한다.
1. Jest 설치
npm i jest --dev

2. package.json의 script에 추가

3. 테스트 실행 
npm run test

1.1 Package.json

  • runInBand: 여러개의 테스트 파일이 오버랩되지 않고 순서대로 테스트하는 옵션(테스트 파일이 동시에 db에 접근하면 충돌이 날 수 있음)
// package.json
{
  "scripts": {
    "test": "env-cmd -f ./config/test.env jest --watch runInBand"
  },
  "jest": {
    "testEnvironment": "node"
  },
	//...
}

1.2 Env File

💡 테스트시, 환경변수 사용하기
  • 환경변수 파일에 mongodb의 url이 포함되어있다면, 해당 파일을 복사한뒤 test.env 와 같이 생성한다.
  • mongodb url이 연결된 원래의 환경변수 파일(dev.env)을 그대로 사용한다면, 테스트시 사용되는 더미데이터가 계속 쌓일테니, 파일을 분리하는 것이다.
    • dev.env: MONGODB_URL=mongodb://localhost/task-manager
    • test.env: MONGODB_URL=mongodb://localhost/task-manager-test
  • package.json의 테스트 스크립트에 해당 경로를 추가한다. (env-cmd ./config/test.env)

2. Testing for Sync Function

  • expect
// math.js
const caculateTip = (total, tipPercent = 0.25) => total + total * tipPercent;

// math.test.js
test('Should calculate total with tip', () => {
  const total = caculateTip(10, 0.3);
  expect(total).toBe(13);
});

3. Testing for Async Function

  • promise / async/await
// math.js
const addAsync = (a, b) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (a < 0 || b < 0) {
        return reject('Numbers must be non-negative');
      }
      resolve(a + b);
    }, 2000);
  });
};

// math.test.js
test('Should add two numbers with promise', done => {
  addAsync(1, 3).then(sum => {
    expect(sum).toBe(4);
    done(); // (*)
  });
});

test('Should add two numbers with async/await', async () => {
  const sum = await addAsync(1, 3);
  expect(sum).toBe(4);
});

4. Testing Express Application 💫

  • npm i supertest --save-dev
  • supertest 라이브러리는 서버가 돌아가지 않아도 테스트가 가능하다.

4.1 순서

  • 테스트 파일에서 app에 접근하기 위해서 index.jsapp.js로 분기
  • 작성한 routers (e.g. user)와 동일하게 테스트 파일 생성
  • mockup 데이터(토큰등 포함)를 생성해서 각 endpoint에 적용
// index.js
const app = require('./app');

app.listen(process.env.PORT, () => {
  console.log('Server is up on ' + process.env.PORT);
});

// app.js
const express = require('express');
require('./db/mongoose'); 
const userRouter = require('./routers/user');
const taskRouter = require('./routers/task');

const app = express();

app.use(express.json()); 
app.use(userRouter);
app.use(taskRouter);

module.exports = app;

// tests/user.test.js
const request = require('supertest');
const app = require('../src/app');

// mockup data
const userOneId = new mongoose.Types.ObjectId();
const userOne = {
  _id: userOneId,
  name: 'Hanna',
  email: 'hanna@gmail.com',
  password: 'hanna1234',
  tokens: [
    {
      token: jwt.sign({ _id: userOneId }, process.env.JWT_SECRET),
    },
  ],
};

test('Should signup a new user', async () => {
  await request(app)
    .post('/users')
    .send({
      name: 'Suyeon',
      email: 'suyeonme@gmail.com',
      password: 'suyeonme1234',
    })
    .expect(201);
});

test('Should get profile for user', async () => { // Authentication
  await request(app)
    .get('/users/me')
    .set('Authorization', `Bearer ${userOne.tokens[0].token}`) // 유효한 토큰일 경우
    .send()
    .expect(200);
});

4.2 Life Cycle Method

아래의 경우, 테스트마다 동일한 이메일이 db에 저장되어 테스트가 실패한다. (모델에서 이메일을 유니크한 값으로 지정해놨기 때문에) 따라서 Jest에서 제공하는 글로벌 함수인, beforeEach를 사용하여 데이터베이스를 클리어한다.

const request = require('supertest');
const app = require('../src/app');
const User = require('../src/models/user');

// 안의 비동기 함수가 끝나면 테스트를 실행한다.
beforeEach(async () => {
  await User.deleteMany();
});

test('Should signup a new user', async () => {
  await request(app)
    .post('/users')
    .send({
      name: 'Suyeon',
      email: 'suyeonme@gmail.com',
      password: 'suyeonme1234',
    })
    .expect(201);
});

4.3 Assertion

response.body를 이용해서 테스트를 진행할 수 있다. 아래의 경우 새로 생성한 유저가 db에 잘 저장되어있는지 테스트하는 코드이다.

test('Should signup a new user', async () => {
  const res = await request(app)
    .post('/users')
    .send({
      name: 'Suyeon',
      email: 'suyeonme@gmail.com',
      password: 'suyeonme1234',
    })
    .expect(201);

  // Assert that the db was changed correctly
  const user = await User.findById(res.body.user._id);
  expect(user).not.toBeNull();

  // Assrtions about the response
  expect(res.body).toMatchObject({
    user: {
      name: 'Suyeon',
      email: 'suyeonme@gmail.com',
    },
    token: user.tokens[0].token,
  });
});

4.4 Testing an image(file)

  • 이미지 업로더를 통해서 이미지를 서버에 보낼경우, 이미지가 buffer 형태로 db에 저장이 잘 되었는지 테스팅
  • test/fixtures 폴더 생성후, 테스트할 이미지를 넣는다.
  • toBe===를 사용하고, toEqual==를 사용한다. 따라서 오브젝트를 비교할 때는 toEqual을 사용해야한다(e.g.{} === {} false).
test('Should upload avatar image', async () => {
  await request(app)
    .post('/users/me/avatar')
    .set('Authorization', `Bearer ${userOne.tokens[0].token}`)
    .attach('avatar', 'tests/fixtures/test-image.png')
    .expect(200);

  const user = await User.findById(userOneId);
  expect(user.avatar).toEqual(expect.any(Buffer)); // (*)
});
profile
Hello World.

0개의 댓글