어제 jest로 서버 쪽에서 API 테스트를 돌렸다. 그리고 오늘 로컬로 client를 돌리는데 npm start를 쳤더니 위와 같은 에러가 나왔다.
6.08.2020
6.07.2020
Jest와 supertest를 이용해서 Node.js API 테스트 하는 법
Jest와 supertest를 이용해서 Node.js API 테스트 하는 법
원래 오늘은 Google Cloud로 배포하는 법을 배우려고 했었다. 하지만 나중에 코드 수정할 때도 그렇고, 지금 배포하기 전에 잘 돌아가는지 한 번 더 확인할 겸 테스트 케이스를 만들기로 했다.
JavaScript를 테스트할 수 있는 기술의 종류는 많다. 그 중에 Jest를 선택한 것은 내가 React를 사용해서 프로젝트를 했으니 같은 Facebook에서 만든 Jest가 좀 더 낫지 않을까 하는 이유에서 선택하게 되었다. supertest는 Jest로 API 테스트 하는 법을 검색하다가 사용하면 좋다는 것을 발견해서 사용하게 되었다.
구체적으로 어떻게 구현했는지 설명하기 전에 TDD(Test Driven Development)에 대해 좀 더 알아본 것을 공유하려고 한다. TDD는 한국말로는 '테스트 주도 개발'이라고 직역하여 부른다. 말 그대로 개발을 함에 있어서 테스트를 중심으로 개발하는 방식을 말한다. 나는 테스트라는 것을 코드를 작성하고 나서, 또는 작성하기 전에 어떤 상황에서 어떤 결과가 나와야 하는지를 실험하기 위해 만든 것이라고 이해하였다. 예를 들어 1 + 1이라는 식을 input으로 집어 넣으면 output은 2가 나와야 통과하는 통과과정을 만든 것이다. 내가 쓴 코드를 테스트로 돌렸을 때 다양한 경우의 input에 대해 제대로 된 output을 도출하는지 확인하기 위한 것이다.
나무위키에서 설명하는 TDD의 장점은 3가지이다. 코드의 유지보수가 쉬워진다는 점, 프로그래밍 시간이 단축된다는 점, 소스코드를 해석할 때 좋은 가이드가 된다는 점이 그것이다. 왜 그런지 자세한 사항은 나무위키를 참조하기 바란다.
나는 코드를 완성한 이후 테스트 코드를 작성했다. 확실히 내가 짠 코드가 테스트를 통과하는 것을 확인하니 배포하기 전에 안심이 되는 느낌이다. 아직 테스트 코드를 다 짠 것은 아니고, 오늘 하루동안 Jest를 배우고 구체적인 적용방법을 검색해서 댓글 기능만 테스트를 만들어 돌렸다. 조금 더 여유를 가지고 나머지 기능들도 테스트를 돌린 후 배포하는 법을 공부해야겠다.
Jest의 사용법은 간단하다. 아래에는 간단한 예제를 적어보았다.
describe('test Jest', () => {
test('should true===true', () => { expect(true).toBe(true);
});
it('should true!==false', () => {
expect(true).toBe(false);
});
});
-
describe
→ 테스트의 한 동작 동작들을 그룹으로 묶어주는 역할을 한다. 예를 들어 댓글기능, 본문 포스팅 기능 등등 여러가지 기능으로 나눌 수 있는데, 구체적인 테스트 코드들을 모두 쭉 나열하기만 한다면 나중에 찾기 어렵다. 이럴 때는 describe 기능으로 크게 크게 구분해주면 나중에 코드를 읽고 수정하기 쉽다.
첫 번째 인자로 설명글이 오고, 두 번째 인자는 구체적인 테스트들이 들어있는 callback이 온다.
-
test, it
→ 하나의 테스트 단위를 말한다. 예를 들어 댓글 기능 중에서도 댓글을 만드는 기능에 대해 테스트 하고 싶을 수 있고, 댓글을 삭제하는 기능에 대해 실험하고 싶을 수도 있다. 이런 세부적인 기능들을 각각의 test 또는 it으로 만들어준다. 둘 중 아무거나 써도 크게 차이는 없는 것 같다.
-
expect(기대하다) , toBe(~이 되기를)
→ 직관적으로 알 수 있듯이, expect 안에 실제값을 넣어주고, toBe 안에 기댓값을 넣어 비교해주는 것이다. 둘이 같을 경우 테스트가 통과되고, 같지 않을 경우 통과되지 않는다. 하나의 test 또는 it 안에는 여러 expect 구문이 있을 수 있다. toBe말고도 ~가 참이 되기를, ~가 거짓이 되기를 등등 여러가지 방법이 있다. 자세한 사항은 구글로 검색해보시기를 바란다.
위의 경우는 이해하기 쉽게 true와 false를 이용해 만들어 보았다. 첫 번째 test는 통과 케이스이고, 두 번째 it은 실패 케이스이다.
Jest 문법은 참 직관적이어서 금방 이해할 수 있었다. 그런데 API를 테스트 할 때 어떻게 예제 URL로 route에 들어가 타고 타고 db까지 바꿀 수 있는지 구체적인 예시가 궁금했다. 뭔가 공식 문서에 나와있는 mock function을 잘 사용하면 될 것 같은데, 관련 부분의 설명이 좀 어려웠다. 그래서 폭풍 구글링을 해 본 결과 supertest라는 패키지를 활용하면 된다는 결론을 얻었다. 바로 설치에서 관련 사이트와 블로그 글들을 참고해서 제대로 된 테스트 케이스를 만들 수 있었다.
const supertest = require('supertest');
const app = require('../server');
const request = supertest(app);
describe('test comments CRUD', () => {
test('create a new test comment', async(done) => {
const test = await request.post("/sample")
.send({
test1: "hello",
test2: "world!"
});
expect(test.status).toBe(302);
done();
});
test('read comments', async(done) => {
const test = await request.get("/sample");
expect(test.status).toBe(200);
done();
});
test('modify a comment', async(done) => {
const test = await request.put("/sample");
expect(test.status).toBe(302);
done();
});
test('delete a test comment', async(done) => {
const test = await request.delete("/sample");
expect(test.status).toBe(302);
done();
});
});
우선 supertest와 app.js를 불러왔다. 그리고 supertest를 통해서 app을 test에서 쓸 수 있게 바꾸어 주었다. 이 때 주의할 것은 app.js에서 module.exports = app;이 되어 있어야 한다는 점이다. 나는 이게 안 되 있어서 한참을 헤메었다.
댓글 기능이라는 공통점이 있기 때문에 하나의 describe를 만들었다. 그리고 CRUD를 테스트하기 위해 4가지의 test를 만들었다. 그리고 callback에는 async와 await을 써서 api에 접근한다. 각 케이스마다 성공할 시 나와야 할 HTTP코드를 확인해준다.
구체적으로 const test에 무엇이 담기는지, 어떤 key값들을 가지고 있는지 Object.keys를 사용해서 일일이 콘솔로그 찍어가며 관찰해보았다. 실제로 많은 기능들이 담겨있었다. 다 검색해보지는 않았지만, 앞으로 알아두면 좋을 것 같다. 뭔지 모르겠고, 어떤 메소드들이 담겨있는지 모르겠으면 콘솔로그로 확인해보는 방법이 좋은 것 같다.
위 코드로 댓글 하나를 생성해서 읽고, 수정하고, 지우는 것까지 db를 통해 확인하였다.
이 글을 읽으시는 분들 중에 지금까지 테스트 코드를 작성해보지 않으셨다면 한 번 작성해보는 것을 권해드리고 싶다.