[Jest] 테스트 전후 작업

@1000peach2022. 05. 29  -  ☕️ 5 min read
[Jest] 테스트 전후 작업

1. beforeEach, afterEach

두 값을 입력받아 더한 결과를 반환하는 함수를 반복해서 테스트 할 때 아래처럼 작성할 수 있다.

fn.ts

const fn = { add: (number1: number, number2: number) => number1 + number2, } export default fn

fn.test.ts

import fn from './fn' let num = 0 // 테스트 성공 --> 결과: 1 test('0 더하기 1은 1이다.', () => { num = fn.add(num, 1) expect(num).toBe(1) }) // 테스트 실패 --> 결과: 3 test('0 더하기 2은 2이다.', () => { num = fn.add(num, 2) expect(num).toBe(2) }) // 테스트 실패 --> 결과: 6 test('0 더하기 3은 3이다.', () => { num = fn.add(num, 3) expect(num).toBe(3) })

하지만 num에 이전 더한 값이 누적되기 때문에 두 번째 케이스부터 테스트를 통과하지 못한다. 즉 모든 테스트를 통과하려면 num을 초기화하는 과정이 필요하다.

이럴 때 각 테스트 전에 실행되는 beforeEach를 사용한다.
아래 코드에서 처음에 num = 10으로 선언해도, 첫 번째 케이스 전 0으로 초기화하기 때문에 모든 테스트를 통과한다.

import fn from './fn' let num = 10 // 테스트 실행 전 초기화되어 값이 누적되지 않는다. beforeEach(() => { num = 0 }) // 테스트 성공 test('0 + 1 = 1', () => { num = fn.add(num, 1) expect(num).toBe(1) }) // 테스트 성공 test('0 + 2 = 2', () => { num = fn.add(num, 2) expect(num).toBe(2) }) // 테스트 성공 test('0 + 3 = 3', () => { num = fn.add(num, 3) expect(num).toBe(3) })

각 테스트 직후에 실행되는 작업은 afterEach로 처리한다.
아래 코드에서는 처음 num = 10으로 선언했기 때문에 0으로 초기화 되는 시점은 첫 번째 케이스 이후 부터이므로, 첫 번째 테스트만 실패하게 된다.

import fn from './fn' let num = 10 // 첫 번째 테스트 이후부터 값이 누적되지 않음 afterEach(() => { num = 0 }) // 테스트 실패 --> 결과: 11 test('0 더하기 1은 1이다.', () => { num = fn.add(num, 1) expect(num).toBe(1) }) // 테스트 성공 test('0 더하기 2은 2이다.', () => { num = fn.add(num, 2) expect(num).toBe(2) }) // 테스트 성공 test('0 더하기 3은 3이다.', () => { num = fn.add(num, 3) expect(num).toBe(3) })

직후 작업이 시간이 걸리는 작업일 때가 있다. 테스트 전 유저 DB에 접속해서 정보를 가져오고, 테스트 후 DB 연결을 끊어야 하는 경우를 작성했다. (각 작업은 0.5초로 가정)

const fn = { add: (number1: number, number2: number) => number1 + number2, // 0.5초 후 DB 유저 정보를 가져옴 connectUserDb: () => { return new Promise(resolve => { setTimeout(() => { resolve({ name: 'Mike', age: 30, gender: 'male', }) }, 500) }) }, // 0.5초 후 DB 연결 해제 disConnectUserDb: () => { return new Promise(resolve => { setTimeout(() => { resolve({}) }, 500) }) }, } export default fn import fn from './fn' let user // 각 테스트 전에는 DB를 연결하고 후에는 연결 해제함 beforeEach(async () => { user = await fn.connectUserDb() }) afterEach(() => { return fn.disConnectUserDb() }) // 테스트 성공 test('이름은 Mike', () => { expect(user.name).toBe('Mike') }) // 테스트 성공 test('나이는 30', () => { expect(user.name).toBe(30) }) // 테스트 성공 test('성별은 male', () => { expect(user.name).toBe('male') })

2. beforeAll, afterAll

위 코드에서 beforeEach, afterEach가 0.5초씩 걸리므로 테스트 당 1초의 시간이 더 걸린다. DB 연결은 모든 테스트 후 해제하는 게 이상적이므로, 연결/해제 작업을 테스트 마다 실행이 아닌 최초/최후 한번만 실행하도록 수정해야 한다.

이 때 전체 테스트 전후로 실행되는 beforeAll, afterAll 매처를 사용한다. DB 연결/해제 작업이 한 번만 실행되어 테스트 시간이 줄어든다.

import fn from './fn' let user // 전체 테스트 전에는 DB를 연결하고 후에는 연결 해제함 beforeAll(async () => { user = await fn.connectUserDb() }) afterAll(() => { return fn.disConnectUserDb() }) ...

3. describe

describe를 이용해서 비슷한 기능을 묶을 수 있다.

const fn = { ... // 0.5초 후 DB 자동차 정보를 가져옴 connectCarDb: () => { return new Promise(resolve => { setTimeout(() => { resolve({ brand: 'bmw', name: 'z4', color: 'red', }) }, 500) }) }, // 0.5초 후 DB 자동차 연결 해제 disConnectCarDb: () => { return new Promise(resolve => { setTimeout(() => { resolve({}) }, 500) }) }, } export default fn import fn from './fn' ... describe('Car 관련 작업', () => { let car beforeAll(async () => { car = await fn.connectCarDb() }) afterAll(() => { return fn.disConnectCarDb() }) test('브랜드는 bmw', () => { expect(car.brand).toBe('bmw') }) test('이름은 z4', () => { expect(car.name).toBe('z4') }) test('색상은 red', () => { expect(car.color).toBe('red') }) })

Car DB 테스트를 추가했다. Car 테스트는 describe 내부에 있으며 beforeAllafterAll은 내부에서만 실행된다.

테스트 설명도 depth로 구분된다.

Car 테스트


4. 실행 순서

지금까지 매처들이 실행되는 순서는 다음과 같다. describe 외부와 내부 테스트로 나누어 작성했다.

import fn from './fn' beforeAll(() => console.log('밖 beforeAll')) // 1 beforeEach(() => console.log('밖 beforeEach')) // 2, 6 afterEach(() => console.log('밖 afterEach')) // 4, 10 afterAll(() => console.log('밖 afterAll')) // 12 (마지막) test('0 + 1 = 1', () => { expect(fn.add(0, 1)).toBe(1) // 3 }) describe('Car 관련 작업', () => { beforeAll(() => console.log(' 안 beforeAll')) // 5 beforeEach(() => console.log(' 안 beforeEach')) // 7 afterEach(() => console.log(' 안 afterEach')) // 9 afterAll(() => console.log(' 안 afterAll')) // 11 (마지막 - 1) test('0 + 1 = 1', () => { expect(fn.add(0, 1)).toBe(1) // 8 }) })
  • describe 내부 테스트(8) 실행 전(6) 외부 beforeEach가 먼저 실행된다.
    • 외부 beforeEach는 해당 파일의 모든 테스트 실행 전에 항상 실행이 되기 때문
  • describe 내부 테스트(8) 실행 후(9)에는 내부 afterEach가 먼저 실행되고, 그 다음 외부 afterEach가 실행(10) 된다.
    • 그 다음 내부 afterAll이 실행(11)되고, 외부 afterAll은 맨 마지막에 실행(12) 된다.

5. only, skip

  • 단독 실행: 특정 테스트만 실행하고 싶을 때 only를 사용한다.
    • 다른 테스트 케이스는 스킵되므로 코드 자체의 이유로 테스트가 실패했는 지 확인하기에 용이하다.
      ex) 실패 시 이전 테스트 영향이 있는 지 확인
import fn from './fn' let num = 0 // 테스트 스킵 test('0 + 1 = 1', () => { expect(fn.add(num, 1)).toBe(1) }) // 테스트 스킵 test('0 + 2 = 2', () => { expect(fn.add(num, 2)).toBe(2) }) // 테스트 성공 (이 테스트만 실행 됨) test.only('0 + 3 = 3', () => { expect(fn.add(num, 3)).toBe(3) })
  • 제외 실행: skip을 붙이면 해당 케이스를 건너뛰고 실행한다.
import fn from './fn' let num = 0 // 테스트 성공 test('0 + 1 = 1', () => { expect(fn.add(num, 1)).toBe(1) }) // 테스트 스킵 (이 테스트만 실행되지 않음) test.skip('0 + 2 = 2', () => { num = 10 expect(fn.add(num, 2)).toBe(2) }) // 테스트 성공 test('0 + 3 = 3', () => { expect(fn.add(num, 3)).toBe(3) })

🔗 참고

이전 게시글

[Jest] 비동기 코드 테스트

다음 게시글

[Jest] 목 함수

profile

@1000peach

    GitHubGmailPortfolio
© 2022 1000peach, Powered By Gatsby.