이전에 [TDD] TDD 시작하기 (Unit Test, Jest)에서 Unit Test에 대해서 알아보고 작성해보았다.
이제 Integration Test에 대해 알아보고자 한다.
Integration Test
통합 테스트란 모듈을 통합하는 단계에서 수행하는 테스트로, Unit Test를 통해 각 모듈들이 잘 작동되는 것을 확인했다면 이제 이 각 모듈들을 연동해서 테스트를 수행해야하는데, 이를 통합 테스트라고 한다.
통합 테스트를 하는 이유
- 모듈들의 상호 작용이 잘 이루어지는지 검증하기 위해
- 통합하는 과정에서 발생할 수 있는 오류를 찾기 위해
Supertest
단위 테스트시에는 Jest를 사용하였는데, 통합 테스트에는 Supertest 모듈을 사용해서 진행할 수 있다.
Supertest는 ExpressJS 통합 테스트용 라이브러리로, 내부적으로 Express 서버를 구동시켜 실제 요청을 보낸 뒤 결과를 검증한다.
기본적인 사용 방법
const request = require('supertest');
const express = require('express');
const app = express();
//클라이언트에서 /user 엔드포인트로 들어오는 요청을 처리하는 원본 소스
app.get('/user', function(req, res){
res.status(200).json({name: 'grace'});
}
//위 원본 소스에 대한 통합 테스트 소스
request(app)
.get('/user')
.expect('Content-Type', /json/)
.expect('Content-Length', '15')
.expect(200)
.end(function(err, res) {
if(err) throw err;
});
통합 테스트 해보기
[TDD] TDD 시작하기 (Unit Test, Jest)
[TDD] node-mocks-http 모듈, Jest의 beforeEach 사용
에 거쳐서 완성된
실제 동작 코드
//route.js
const express = require('express');
const router = express.Router(); //express.Router 클래스를 이용해서 Router 모듈로 작성하기
const productController = require('./controller/products');
//Route 정의
router.post('/', productController.createProduct);
// controller/products.js
const productModel = require('../models/Product');
exports.createProduct = async (req, res, next) => {
try {
const createdProduct = await productModel.create(req.body);
res.status(201).json(createdProduct);
} catch (error) {
next(error);
}
};
에 대한 통합 테스트를 진행해보자.
유닛 테스트 단계에서는 Database의 작동이 정상적이라는 가정 하에 진행하였으므로 Mock 함수로 이를 대체하였지만
통합 테스트 단계에서는 실제 API의 작동이 잘 되는지를 테스트해보는 것이기 때문에 실제로 API요청을 실제 동작 코드로 보내보고, Database도 잘 작동하는지 확인을 해봐야 한다.
1) 통합테스트를 위한 폴더 생성
통한 테스트를 위한 integration 폴더
- 통합 테스트 파일 [대상 이름].test.init.js
2) 통합 테스트 작성하기
//products.init.test.js
const request = require('supertest');
const app = require('../../server');
const newProduct = require('../data/new-product.json'); // 테스트용으로 넣어줄 데이터
// 통합 테스트케이스 만들기
it('POST /api/products', async () => {
//supertest
const response = await request(app) //해당 API결과 비동기로 결과를 반환해주기 때문에 여기서도 받아줘야하며
.post('/api/products') //해당 API의 엔드포인트와 요청이 이것이므로
.send(newProduct); // 해당 API에 보내줘야하는 것이 있으므로 (const createdProduct = await productModel.create(req.body);)
//이렇게 했을 때 예상 결과가,
expect(response.statusCode).toBe(201); // API 결과 상태코드가 201이어야 하므로 (res.status(201).json(createdProduct);
expect(response.body.name).toBe(newProduct.name);
expect(response.body.description).toBe(newProduct.description);
});
3) 에러 처리를 위한 통합 테스트 작성
먼저 에러 헨들러가 없다면 에러 헨들러(미들웨어)부터 만들어준다.
//server.js
app.use((error, req, res, next) => {
res.status(500).json({message: error.message}))
}
그리고 에러 처리를 위한 통합 테스트를 작성해준다.
//products.init.test.js
//에러 처리를 위한 통합 테스트케이스
// 즉, catch (error) {
// next(error);
// } 이 부분이 잘 처리되고 있는지를 검사하는 테스트케이스 이다.
it('should return 500 on POST /api/products', async () => {
request(app).post('/api/products').send({ nema: 'error발생용 데이터' }); //에러를 발생시켜야 하니까 잘못된 형태의 데이터를 보내보자
//이렇게 했을 때 예상 결과가,
expect(response.statusCode).toBe(500); // 서버에러가 발생하기 때문에 500
expect(response.body).toStrictEqual({
message: 'Product validation failed: description: Path `description` is required.',
}); //서버에러 발생시 에러 메세지
});
결과적으로
실제 동작 코드
//route.js
const express = require('express');
const router = express.Router(); //express.Router 클래스를 이용해서 Router 모듈로 작성하기
const productController = require('./controller/products');
//Route 정의
router.post('/', productController.createProduct);
// controller/products.js
const productModel = require('../models/Product');
exports.createProduct = async (req, res, next) => {
try {
const createdProduct = await productModel.create(req.body);
res.status(201).json(createdProduct);
} catch (error) {
next(error);
}
};
에 대한 통합 테스트 소스 코드는
const request = require('supertest');
const app = require('../../server');
const newProduct = require('../data/new-product.json'); // 테스트용으로 넣어줄 데이터
// 통합 테스트케이스 만들기
it('POST /api/products', async () => {
//supertest
const response = await request(app) //해당 API결과 비동기로 결과를 반환해주기 때문에 여기서도 받아줘야하며
.post('/api/products') //해당 API의 엔드포인트와 요청이 이것이므로
.send(newProduct); // 해당 API에 보내줘야하는 것이 있으므로 (const createdProduct = await productModel.create(req.body);)
//이렇게 했을 때 예상 결과가,
expect(response.statusCode).toBe(201); // API 결과 상태코드가 201이어야 하므로 (res.status(201).json(createdProduct);
expect(response.body.name).toBe(newProduct.name);
expect(response.body.description).toBe(newProduct.description);
});
//에러 처리를 위한 통합 테스트케이스
// 즉, catch (error) {
// next(error);
// } 이 부분이 잘 처리되고 있는지를 검사하는 테스트케이스 이다.
it('should return 500 on POST /api/products', async () => {
request(app).post('/api/products').send({ nema: 'error발생용 데이터' }); //에러를 발생시켜야 하니까 잘못된 형태의 데이터를 보내보자
//이렇게 했을 때 예상 결과가,
expect(response.statusCode).toBe(500); // 서버에러가 발생하기 때문에 500
expect(response.body).toStrictEqual({
message: 'Product validation failed: description: Path `description` is required.',
}); //서버에러 발생시 에러 메세지
});
가 된다.
reference
따라하며 배우는 TDD 개발 - John Ahn
'Dev > SW Engineering' 카테고리의 다른 글
[TDD] Python Testing Framework2 - Pytest, Unittest - mocking (0) | 2021.08.16 |
---|---|
[TDD] Python Testing Framework 1 - Pytest, unittest (0) | 2021.05.21 |
[TDD] node-mocks-http 모듈, Jest의 beforeEach 사용 (0) | 2021.02.24 |
[TDD] TDD 시작하기 (Unit Test, Jest) (0) | 2021.02.22 |
40. Authentication - OAuth 2.0 (0) | 2020.09.14 |