개발

Jest 사용법 (Usage)

flowertaekk 2021. 5. 30. 20:59

JestEnzyme는 프론트엔드 Unit test, Integration test 에 사용되는 프레임워크와 라이브러리입니다.
오늘은 Jest를 이용해서 테스트를 하기 위한 기본 사용법에 대해서 정리해보겠습니다.


Jest의 기본 문법부터 알아볼까요!

 

 

expect().toBe() 등과 같은 assertion 함수들이 많이 존재하지만, 여기에서 다 다루기에는 양도 방대하고, 필요에 따라서 검색을 통해 알아가는게 더 효율적이라 생각이 들어서 공식문서 링크만 공유하겠습니다.

그림으로 동작하는 방식만 간단히 정리해볼까요?


Jest의 실행 방법은?


공식문서에는 커맨드라인에서 실행하는 법도 나와있지만, 보통은 아래와 같이 package.json에 등록한 후 사용합니다!

{ "scripts":
  {
    "test": "jest", // 한 번 실행
    "test:watch": "jest --watch", // 코드 변경시마다 테스트가 자동 실행
    "test:coverage": "jest --coverage" // 커버리지 테스트
  }
}


실행 방법은 아래와 같습니다.

  • npm run test
  • npm run test:watch
  • npm run test:coverage
  • yarn test
  • yarn test:watch
  • yarn test:coverage

그럼 테스트에서 빼놓을 수 없는 Mock 은 어떻게 할까요?

개발환경에서 server를 켜놓지 않았다면 server랑 통신하는 테스트는 모두 실패할테니까, 서버랑 통신하는 부분을 mock 해야만 합니다. 

사실 어느 언어에서도 mock 없는 테스트는 상당히 고단하겠죠? (mock 개념에 대한 자료는 이미 블로깅 등이 많아서 따로 설명하지 않을게요!)

 

Jest에서도 mock 기능을 3가지 방식으로 제공합니다.

  1. jest.fn() : 함수 하나를 mock합니다.
  2. jest.mock() : 하나의 모듈 안에 있는 모든 함수들을 mock합니다.
  3. jest.spyOn() : mock() 과 동일하지만, mock의 대상 함수의 원래 동작도 유지합니다. (원래 구현을 테스트할 수도 있고, mock을 통해 구현 내용을 바꾼 상태로 테스트 할 수도 있다는 의미입니다. 아래에서 좀 더 자세히 설명할게요!)

한 번 각자 어떻게 동작하는지 봐볼까요?

1. jest.fn()

// just.fn()

// app.js
export function sayHello(callback) {
  // some logics..
  callback()
}


// test.js
import * from 

// given
const mock = jest.fn(() => {console.log("mocked!!")})

// when
sayHello(mock)

// then
expect(mock).toHaveBennCalledWith(1)

이 방법은 솔직히 언제 써야할지 잘 감이 안 잡히네요.  조언 해주실 분은 댓글로 부탁드립니다!

 

2. jest.mock()

// jest.mock()

// app.js
export const hello = () => { console.log('hello') }
export const world = () => { console.log('world') }

// test.js
jest.mock("./app.js") // app.js를 mock함으로써 app.js가 아래와 같이 변함.

export const hello = () => jest.fn()
export const world = () => jest.fn()

jest.fn() 을 직접 쓰기보다는 위와 같이 jest.mock()과 아래에 나오는 jest.spyOn()이라는 Syntactic sugar를 통해 간편히 사용하는 방법을 더 많이 사용할 것 같습니다.

 

3. jest.spyOn()

// jest.spyOn()

// app.js
export const hello = () => { console.log('hello') }
export const world = () => { console.log('world') }

// test.js
import * as app from "./app"

test("mock world", () => {
  const mockWorld = jest.spyOn(app, 'world') // 이것만으로는 app.js 코드가 mock 되지 않음.
  mockWorld.mockImplementation(() => {console.log('mocked world')}) // 이 코드를 통해서 app.js 코드를 mock 하고 아래와 같이 변함. 
})

export const hello = () => { console.log('hello') }
export const world = () => { console.log('mocked world') }

 

jest.spyOn()은 mock 대상 함수를 지정하는 것이 가능하고, 지정되지 않은 함수는 기존의 로직을 유지합니다! 또한, mock을 해제하는 것도 가능합니다! (mockworld.mockRestore())

 

저는 Dylan님의 블로그가 실질적인 예시와 함께 잘 설명해주셔서 이해하기 가장 편했습니다!

위의 내용도 Dylan님의 블로그을 참고로 작성했습니다.

 

그래서 저는 어떻게 API call 을 mock 했냐면요.

// index.tsx
export const _healthChecker = async (setResultCallback: any) => {
  const result = await callByGET("/healthcheck")
  setResultCallback(result.status)
}

const IndexPage = () => {
  const [healthCheckStatus, setHealthCheckStatus] = useState('undefined')

  return (
    <Layout>
      <h1 className='healthcheck__btn' onClick={() => _healthChecker(setHealthCheckStatus)}>{`Health check : ${healthCheckStatus}`}</h1>
    </Layout>
  )

}
export default IndexPage

// IndexPage.test.tsx
import 'jsdom-global/register'
import { mount } from 'enzyme'
import IndexPage from '../index'
import * as index from '../index'

test('healthcheck works', async () => {
  // given
  const _healthCheckerMock = jest.spyOn(index, "_healthChecker")
  _healthCheckerMock.mockImplementation(async (setResultCallback) => {
    setResultCallback('healthy')
  })

  const wrapper = mount(<IndexPage />)
  const healthcheckBtn = wrapper.find('.healthcheck__btn')
  expect(healthcheckBtn.text()).toBe('Health check : undefined')

  // when
  healthcheckBtn.simulate('click')

  // then
  expect(healthcheckBtn.text()).toBe('Health check : healthy')
})

Jest로 테스트하면서 발생했던 트러블은?

1. Jest가 css파일을 인식하지 못해서 테스트가 돌아가지 않는 문제가 있었습니다.

해결방법 : css 모듈 파일을 mock 함으로써 트러블 해결 참고 자료

  1. 프로젝트 root 에 __mocks__ 디렉토리 생성.
    1. 디렉토리명은 중요하지 않습니다! (자유)
  2. 프로젝트root/__mocks__/styleMock.js 생성.
    1. 파일명 역시 중요하지 않습니다! (자유)
  3. 프로젝트root/__mocks__/styleMock.js 수정 (아래 내용 추가)
    module.exports={}
  4. package.json에 다음 설정 추가
    {
       "jest":{
            "moduleNameMapper":{
                "\\.(css|less|sass|scss)$": "<rootDir>/__mocks__/styleMock.js"
            }
       }
    }​

저는 새로운 언어를 공부할 때 테스트를 어떻게 구현하는지를 공부하면서 익히는 편인데요!

새로운 언어로 테스트를 작성하다보면, 그 언어의 특징을 자연스레 알게되고, 어떤 식으로 아키텍처를 잡아야 좋을지를 머리속으로 자연스럽게 그리게 됩니다! (저는 테스트가 가능한 코드를 짜기 위해서라면 언어가 제공하는 장점을 살짝 무시하는 것 정도는 자주 하고 있거든요! 하하)

 

오늘도 읽어주셔서 감사합니다!

'개발' 카테고리의 다른 글

target="_blank" 다른 탭으로 링크 이동하기  (0) 2021.11.12
프론트엔드 vs. 백엔드 테스트  (0) 2021.05.22
Java 접근 제한자 (Access Modifier)  (0) 2021.05.15
concourse 테스트  (0) 2021.05.05
circle-ci 테스트  (0) 2021.04.30