<Project 3 - Richmaker >
기간 : 3주
팀원 : 프론트 2명 (PM) / 백엔드 5명
필수 구현 사항 : 로그인, 회원가입, 테스트 코드, 메인페이지, 공동관리 페이지, 마이데이터(구현), 거래내역 리스트
기반은 마무리 되었고 코드 작성을 시작했다.
시작과 동시에 멘토님들이 이번 프로젝트부터는 테스트코드를 작성해서 TDD 방식의 작업을 요구하셨다.
TDD란 Test Driven Development의 약자로 ‘테스트 주도 개발’로써
내가 이해한바로는 테스트를 먼저 작성 후 그 테스트를 통과하게끔 코드를 작성한다. 즉 결과를 미리 예측하고 작업을 해야한다고 이해했다.
팀원들 대부분 테스트코드에 대해 어려움이 많았고
글쓴이가 대표로 테스트 코드 템플릿을 작성하기로 했다.
기본적인 로그인 회원 가입으로 진행했고
test 를 위한 mock 데이터를 담은 userDate.js
테스트코드 를 위한 userTest.js
이렇게 구성하였다.
더 심화된 fixture.js를 사용하기도 하지만 basic 한 기본 테스트이기 때문에 두 가지 파일이면 충분했다.
const request = require("supertest");
const { createApp } = require("../../app");
const { AppDataSource } = require("../../api/models/dataSource");
const userTestData = require("../data/userData");
describe("userTest", () => {
let app;
beforeAll(async () => {
app = createApp();
await AppDataSource.initialize();
});
afterAll(async () => {
await AppDataSource.query("SET FOREIGN_KEY_CHECKS=0");
await AppDataSource.query(`TRUNCATE users`);
await AppDataSource.query(`ALTER TABLE users AUTO_INCREMENT = 1`);
await AppDataSource.query("SET FOREIGN_KEY_CHECKS=1");
await AppDataSource.destroy();
});
test("preSignIn : SUCCESS", async () => {
await request(app).post("/user/signup").send(userTestData.presignIn[3]);
const response = await request(app)
.post("/user/presignin")
.send(userTestData.presignIn[0]);
expect(response.statusCode).toEqual(201);
expect(response.body).toEqual({ message: "user is confirmed" });
});
test("preSignIn : not phoneNumber", async () => {
const response = await request(app)
.post("/user/presignin")
.send(userTestData.presignIn[1]);
expect(response.statusCode).toEqual(400);
expect(response.body).toEqual({ message: "not phoneNumber" });
});
test("preSignIn: INVALID_USER", async () => {
const response = await request(app)
.post("/user/presignin")
.send(userTestData.presignIn[2]);
expect(response.statusCode).toEqual(200);
expect(response.body).toEqual({ message: "INVALID_USER" });
});
test("signUp : SUCCESS", async () => {
const response = await request(app)
.post("/user/signup")
.send(userTestData.signUp[0]);
expect(response.body).toEqual({ message: "user is created" });
expect(response.statusCode).toEqual(201);
});
test("signUp : KEY_ERROR_USERNAME", async () => {
const response = await request(app)
.post("/user/signup")
.send(userTestData[1]);
expect(response.statusCode).toEqual(400);
expect(response.body).toEqual({ message: "KEY ERROR" });
});
test("signUp : KEY_ERROR_PASSWORD", async () => {
const response = await request(app)
.post("/user/signup")
.send(userTestData[2]);
expect(response.statusCode).toEqual(400);
expect(response.body).toEqual({ message: "KEY ERROR" });
});
test("signUp : KEY_ERROR_PHONENUMBER", async () => {
const response = await request(app)
.post("/user/signup")
.send(userTestData[3]);
expect(response.statusCode).toEqual(400);
expect(response.body).toEqual({ message: "KEY ERROR" });
});
test("signIn : SUCCESS", async () => {
const response = await request(app)
.post("/user/signin")
.send(userTestData.signIn[0]);
expect(response.body).toHaveProperty(
"accessToken",
"userName",
"phoneNumber",
"profileImage"
);
expect(response.statusCode).toEqual(201);
});
test("signIn : KEY_ERROR_PHONENUMBER", async () => {
const response = await request(app)
.post("/user/signin")
.send(userTestData.signIn[1]);
expect(response.statusCode).toEqual(400);
expect(response.body).toEqual({ message: "KEY ERROR" });
});
test("signIn : KEY_ERROR_PASSWORD", async () => {
const response = await request(app)
.post("/user/signin")
.send(userTestData.signIn[2]);
expect(response.statusCode).toEqual(400);
expect(response.body).toEqual({ message: "KEY ERROR" });
});
});
그 동안에는 app.js 하나로 이니셜라이즈와 서버 구동 등을 모두 처리 했지만 테스트를 돌렸을때 실제 서버가 돌아가면 안되기 때문에 application 역할을 담당하는 app.js 와 server 구동을 위한 server.js 로 나누었다.
테스트는 supertest 패키지를 사용했고
그 외 필요한 것들을 require 해주었다.
테스트 코드 안에 구조는
describe( testname , () => {})
이름과 callback function 이 들어있는 큰 박스라고 표현한다.
그 안에
beforeAll()
afterAll()
test()
이렇게 3가지로 나뉜다.
beforeAll 은 테스트를 돌리기 위한 밑작업 정도로 이해하면 좋다.
먼저 application을 먼저 구동 시켜주고 그 다음으로
예를 들어 로그인을 한다고 가정한다면 이미 고객의 정보가 있어야 데이터를 확이하고 로그인을 진행할텐데 실제로 사용하는 DB로 테스트를 한다면 중요한 고객의 정보가 유실되거나 없는 고객의 정보가 들어가기 때문에
테스트용 DB로 테스트를 해야하는데 그 곳에는 고객의 데이터가 없기 때문에 beforeAll 을 통해서 DB에 mock 데이터를 넣어준다. 이후 test code 가 돌아가면서 로그인 코드를 실행할 수 있게 된다.
그 다음으로 입력되고 사용한 테스트용 mock 데이터를 지워야한다.
그 작업을 위해 afterAll 이 작동이 된다
원래 데이터를 지우는 query로 delete drop 등 이 있지만
auto_increment 며 사용한 흔적이 있기 때문에 truncate 를 사용하여 테이블 자체를 한번 지우고 다시 생성하게 afterAll 에 넣어준다.
beforeAll 과 afterAll 사이에 드디어 testcode가 들어간다.
test("preSignIn : SUCCESS", async () => {
await request(app).post("/user/signup").send(userTestData.presignIn[3]);
const response = await request(app)
.post("/user/presignin")
.send(userTestData.presignIn[0]);
expect(response.statusCode).toEqual(201);
expect(response.body).toEqual({ message: "user is confirmed" });
});
test("preSignIn : not phoneNumber", async () => {
const response = await request(app)
.post("/user/presignin")
.send(userTestData.presignIn[1]);
expect(response.statusCode).toEqual(400);
expect(response.body).toEqual({ message: "not phoneNumber" });
});
test("preSignIn: INVALID_USER", async () => {
const response = await request(app)
.post("/user/presignin")
.send(userTestData.presignIn[2]);
expect(response.statusCode).toEqual(200);
expect(response.body).toEqual({ message: "INVALID_USER" });
});
현재 프로젝트에서는 presignin 이라는 과정이 있기 때문에 데이터가 필요했다. 그래서 beforeAll 을 사용해서 데이터를 미리 넣으려고 했지만 문제가 생겼다.
beforeAll 의 경우 afterAll이 작동되기 전까지 데이터가 계속 남아있기 때문에 예를 들자면 회원가입 과정에서 중복된 회원 정보라고 해서 회원가입이 안 될수도 있다. 이 예시 외에도 예상치 못한 예시가 많이 생길수도 있기 때문에 이러한 잠깐을 위한 정보는 코드가 길어지겠지만 test()안에 넣어서 해결해야했다.
먼저 작성한 회원가입 코드를 먼저 실행시켜서 정보를 입력하고 그 다음으로 presignin 코드를 실행할 수 있게 test()안에 두 가지의 코드를 넣었다.
이렇게 signin 관련 테스트 코드들을 작성하여 성공에 관련한 테스트를 success 받았고 그 외 실패에 관한 error 핸들링을 통해 error message를 설정해놓고 결과값으로 그 message가 나오게 작성한 후 테스트를 돌리면 원하는 실패를 유도 했기 때문에 success 를 받았다.
이렇게 작성한 테스트 코드들을 기준으로 팀원들에게 테스트코드에 대해 설명하며 담당 티켓을 한가지 끝냈다.