728x90
실제 객체를 만들어 사용하기에 시간, 비용 등의 Cost가 높거나 혹은 객체 서로간의 의존성이 강해 구현하기 힘들 경우 가짜 객체를 만들어 사용하는 방법입니다.
필요한 순간
- 테스트 작성을 위한 환경 구축이 어려운 경우
- 테스트가 특정 경우나 순간에 의존적인 경우
- 테스트 시간이 오래 걸리는 경우
ProductService.js
const ProductClient = require('./product_client')
class ProductService {
constructor() {
// 좋지 못한 코드 클래스 내부에서 의존성을 만들어주는 것은 OOP설계 원칙을 벗어나는 행위
this.productClient = new ProductClient()
}
fetchAvailableItems() {
return this.productClient
.fetchItems()
.then(items => items.filter(item => item.available))
}
}
module.exports = ProductService
ProductClient.js
class ProductClient {
fetchItems() {
return fetch('http://example.com/login/id').then(response =>
response.json()
)
}
}
module.exports = ProductClient
위 코드를 보면 네트워크 통신을 통해 데이터를 받아오는 함수 입니다.
이러한 경우 테스트에서 직접적으로 함수를 쓰는 것은 좋지 않습니다.
그 이유는 데이터를 받아오지 못하는 경우 또는 일시적으로 네트워크에 문제가 생긴다면 테스트 케이스는 실패하게 됩니다.
그렇기 때문에 mock을 통해 가짜 객체를 만들어 줍니다.
const ProductService = require('../product_service_no_di.js')
const ProductClient = require('../product_client.js')
// 실제 product_client를 사용하는 것이 아닌 가짜를 사용합니다.
jest.mock('../product_client')
// 단위 테스트는 서로 모듈간의 상호작용을 테스트하면 안되고 그 단위 하나만 테스트해야합니다.
describe('ProductService', () => {
// fetchItems를 호출하면 mock함수가 콜백함수를 호출해줍니다.
const fetchItems = jest.fn(async () => [
{ item: '🥛', available: true },
{ item: '🍌', available: false },
])
// ProductClient를 호출하면 fetchItems가 실행됩니다.
ProductClient.mockImplementation(() => {
return {
fetchItems,
}
})
let productService
beforeEach(() => {
productService = new ProductService()
fetchItems.mockClear()
ProductClient.mockClear()
})
it('should filter out only available items', async () => {
// productService.fetchAvailableItems()를 실행하면 fetchItems에 있는 아이템중에서
// available이 true인 값만 리턴
const items = await productService.fetchAvailableItems()
expect(items.length).toBe(1)
expect(items).toEqual([{ item: '🥛', available: true }])
})
it('test', async () => {
const items = await productService.fetchAvailableItems()
// 함수가 호출된 횟수
expect(fetchItems).toHaveBeenCalledTimes(1)
})
})
728x90