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
내부에 있으며 beforeAll
과 afterAll
은 내부에서만 실행된다.
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)
})