TDD

mock

테오구 2022. 4. 19. 18:40
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

'TDD' 카테고리의 다른 글

FIRST 원칙  (0) 2022.04.25
좋은 테스트 코드  (0) 2022.04.23
stub  (0) 2022.04.22
basic  (0) 2022.04.19
TDD란  (0) 2022.04.19