* 개요
앞 글에서, mocha, should 사용법을 알아보았습니다. 이제, 실제 API 서버를 어떻게 TDD로 개발할 수 있는지 실습합니다. HTTP 메서드인 GET, POST, PUT, DELTE 4가지를 실습하고 예외 상황도 검사합니다.
1. GET /users
- 성공시 유저객체를 담은 배열로 응답한다
- 성공시 최대 limit 갯수만큼 응답한다.
- 실패시 limit이 숫자형이 아니면 400을 응답한다
//index.spec.js
const request = require("supertest");
const should = require("should");
const app = request("./index");
describe('GET /users 는', () => {
describe('성공시', () => {
it('유저 객체를 담은 배열로 응답한다', (done) => {
request(app)
.get('/users')
.end((err,res) => {
res.body.should.be.instanceOf(Array);
done();
});
});
it('최대 limit 갯수만큼 응답한다', (done) => {
request(app)
.get("/users?limit=2")
.end((err, res) => {
res.body.should.have.lengthOf(2);
done();
});
});
};
describe('실패시', () => {
it('limit이 숫자형이 아니면 400을 응답한다', (done) => {
request(app)
.get("/users?limit=two")
.expect(400)
.end(done);
});
});
};
//index.js
const express = require("express");
const app = express();
const morgan = require("morgan");
let users = [
{id: 1, name: 'alice'},
{id: 2, name: 'bak'},
{id: 3, name: 'chris'},
];
app.use(morgan('dev'));
app.get("/", function(req, res) {
res.json(user);
});
app.get("/users", function(req, res) {
req.query.limit = req.query.limit || 10;
const limit = parseInt(req.query.limit, 10);
if(Number.isNaN(limit)) {
return res.status(400).end();
}
return res.json(users.slice(0, limit));
});
2. GET /users/:id
- 성공시 id가 1인 유저 객체를 반환한다
- 실패시 id가 숫자가 아닐경우 400을 응답한다
- 실패시 id로 유저를 찾을 수 없는 경우 404를 응답한다
//index.spec.js
describe('GET /users/1', () => {
describe('성공시', () => {
it('id가 1인 유저 객체를 반환한다', done => {
request(app)
.get('/users/1')
.end((err, res) => {
res.body.should.have.property('id', 1);
done();
});
});
});
describe('실패시', () => {
it('id가 숫자가 아닐경우 400으로 응답한다', (done) => {
request(app)
.get('/users/one')
.expect(400)
.end(done);
});
it('id로 유저를 찾을 수 없는 경우 404로 응답한다', (done) => {
request(app)
.get('/users/999')
.expect(404)
.end(done);
});
});
});
//index.js
app.get("/users/:id", function(req, res) {
const id = parseInt(req.query.id, 10);
if(Number.isNaN(id)) return res.status(400).end();
const user = users.filter(user => user.id === id)[0];
if(!user) return res.status(404).end();
return res.json(user);
});
3. POST /users
- 성공시 생성된 유저 객체를 반환한다
- 성공시 입력한 name을 반환한다
- 실패시 name 파라미터 누락시 400을 반환한다
- 실패시 name이 중복일 경우 409를 반환한다
describe("POST /users", () => {
describe("성공시", () => {
let name = 'daniel';
let body;
before(done => {
request(app)
.post("/users")
.send({name})
.expect(201)
.end((err, res) => {
body = res.body;
done();
});
});
});
it("생성된 유저 객체를 반환한다", () => {
body.should.have.propety('id');
});
it("입력한 name을 반환한다", () => {
body.should.have.propety('name', name);
});
describe("실패시", () => {
it('name 파라미터 누락시 400을 반환한다', (done) => {
request(app)
.post("/users")
.send({})
.expect(400)
.end(done);
});
it('name이 중복일 경우 409를 반환한다', (done) => {
request(app)
.post("/users")
.send({})
.expect(400)
.end(done);
});
});
});
//index.js
app.post("/users", function(req, res) {
const name = req.body.name;
if(!name) return res.status(400).end();
const isConflicted = users.filter(user => user.name === name).length;
if(isConflicted) return res.status(409).end();
const id = Date.now();
const user = {id, name};
users.push(user);
return res.status(201).json(user);
});
4. PUT /:id
- 성공시 변경된 name을 응답한다
- 실패시 정수가 아닌 id일 경우 400을 응답한다
- 실패시 name이 없을 경우 400을 응답한다
- 실패시 없는 유저일 경우 404를 응답한다
- 실패시 이름이 중복일 경우 409를 응답한다
//index.spec.js
describe('PUT /users/:id', () => {
describe('성공 시', () => {
it('변경된 name을 응답한다', done => {
const name = 'chally';
request(app)
.put("/users/3")
.send({name})
.end((err,res) => {
res.body.should.have.property('name', name);
done();
});
});
});
describe('실패 시', () => {
it('정수가 아닌 id일 경우 400을 응답한다', done => {
request(app)
.put("/users/three")
.expect(400)
.end(done);
});
it('name이 없을 경우 400을 응답한다', done => {
request(app)
.put("/users/3")
.send({})
.expect(400)
.end(done);
});
it('없는 유저일 경우 404를 응답한다', done => {
request(app)
.put("/users/999")
.expect(404)
.end(done);
});
it('이름이 중복인 경우 409를 응답한다', done => {
request(app)
.put("/users/3")
.send({name: 'bak'})
.expect(409)
.end(done);
});
});
});
//index.js
app.put("/users/:id", (req,res) => {
const id = parseInt(req.params.id, 10);
if(Number.isNaN(id)) return res.status(400).end();
const name = req.body.name;
if(!name) return res.status(400).end();
const user = users.filter(user => user.id === id);
if(!user) return res.status(404).end();
const isConflicted = users.filter(user => user.name === name).length;
if(isConflicted) returnres.tatus(409).end();
user.name = name;
return res.json(user);
});
5. DELETE /users/:id
- 성공시 204를 응답한다
- 실패시 id가 숫자가 아닐경우 400을 응답한다
describe('DELETE /users/1', () => {
describe('성공 시', () => {
it('204를 응답한다', done => {
request(app)
.delete('/users/1')
.expect(204)
.end(done);
});
});
describe('실패 시', () => {
it('id가 숫자가 아닐경우 400을 응답한다', done => {
request(app)
.delete('/users/one')
.expect(400)
.end(done);
});
});
});
//index.js
app.delete("/users/:id", (req,res) => {
const id = parseint(req.params.id, 10);
if(Number.isNaN(id)) return res.status(400).end();
users = users.filter(user => user.id !== id);
res.status(204).end();
});
* 정리
HTTP 4가지 메서드의 성공과 실패를 나누고 각각 테스트를 한다.
매개변수의 id값, body에 전송되는 name 등의 유효성을 검증해야한다.
반응형
'학습 > Node.js' 카테고리의 다른 글
노드 ORM 시퀄라이저 (0) | 2022.01.17 |
---|---|
라우터 컨트롤러 함수로 분리 (0) | 2022.01.17 |
mocha, should, supertest 사용하기 (0) | 2022.01.15 |
강의 후기 (0) | 2022.01.15 |
node js 데이터베이스 이용하기 (0) | 2022.01.15 |