2020-3-14 Jest(2)
Testing asynchronous code
- js코드를 비동기적으로 실행하는것은 최근 공통된 일이다.
callbacks
- 많이 사용되는 비동기 패턴은 콜백. fetchData(callback)는 callback(data)를 fetch한다.
// Don't do this!
test('the data is peanut butter', () => {
function callback(data) {
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
// Right thing
test('the data is peanut butter', done => {
function callback(data) {
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}
fetchData(callback);
});
- 위에 있는 예시로 하면 callback이 실행되기전에 fetchData가 끝난다.
- test인자에 done을 전달한다. Jest는 done이 callback될때까지 test를 끝내지 않고 기다린다 !!!
- done이 호출되지 않으면 테스트는 timeout error를 호출하면서 실패한다.
- 테스트가 왜 실패됬는지 확인하기 위해 try-catch를 사용하고 catch문 안에서 done(error)를 쓰면된다.
Promise
- promise를 사용하면 비동기 테스트를 직접적으로 다룰 수 있다. jest는 promise가 resolve될때까지 기다린다. promise가 reject되면 jest는 자동으로 실패가 된다.
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(e => expect(e).toMatch('error'));
});
test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});
test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});
- .resolve, .reject를 이용해서 기대값을 매칭시킬 수도 있다.
async/await
- test함수에 async키워드를 붙여서 비동기 처리를 할 수 있다. 또한 async/await키워드에 .resolves, .reject를 조합할수도 있다.
test('the data is peanut butter', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});
test('the fetch fails with an error', async () => {
expect.assertions(1);
try {
await fetchData();
} catch (e) {
expect(e).toMatch('error');
}
});
test('the data is peanut butter', async () => {
await expect(fetchData()).resolves.toBe('peanut butter');
});
test('the fetch fails with an error', async () => {
await expect(fetchData()).rejects.toThrow('error');
});
Setup and teardown
- 테스트를 실행전에 설정을 반복적인 설정, 한번설정. 테스트가 끝나고 실행되는 환경을 구축할 수 있다.
- beforeEach(), afterEach()의 매번 테스트가 실행될때마다 실행전후로 실행이된다.
- 데이터베이스 테스트의 경우 사전에 connection이 되어야한다. 그리고 connectio들도 promise나 async/await을 사용하는 경우가 많기때문에 beforeEach()에서 설정을 하고 비동기처리의 경우 done을 활용한다.
beforeEach(() => {
initializeCityDatabase();
});
afterEach(() => {
clearCityDatabase();
});
test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});
test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});
// other case
beforeAll(() => {
return initializeCityDatabase();
});
afterAll(() => {
return clearCityDatabase();
});
- 테스트 시작전에 file을 읽어야되는 경우, beforeAll(), afterAll()을 활용해서 모든테스트가 동작하기 사전에 설정할수도 있다.
scoping
- 기본적으로 before, after블록은 모든 테스트파일에 적용된다. 뿐만 아니라 describe로 테스트의 그룹화 역시할 수 있다. describe를 사용해서 테스트 그룹화를 하면 after, before는 해당 describe에서만 사용할 수 있다 !!
// Applies to all tests in this file
beforeEach(() => {
return initializeCityDatabase();
});
test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});
test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});
describe('matching cities to foods', () => {
// Applies only to tests in this describe block
beforeEach(() => {
return initializeFoodDatabase();
});
test('Vienna <3 sausage', () => {
expect(isValidCityFoodPair('Vienna', 'Wiener Schnitzel')).toBe(true);
});
test('San Juan <3 plantains', () => {
expect(isValidCityFoodPair('San Juan', 'Mofongo')).toBe(true);
});
});
- 바깥의 beforeEach의 경우 describe안에 있는 beforeEach가 실행되기전에 실행된다. beforeAll()의 경우 beforeEach()보다 먼저 실행이 된다.
order of execution of describe and test blocks
- jest는 실제 test()를 실행하기 전에 모든 describe handler를 실행한다. 이것은 환경설정을 decribe블록안 대신 before* after*에 해야되는 이유.
- describe 모두 실행되면 jest는 순차적으로 모든 테스트를 실행한다.
describe('outer', () => {
console.log('describe outer-a');
describe('describe inner 1', () => {
console.log('describe inner 1');
test('test 1', () => {
console.log('test for describe inner 1');
expect(true).toEqual(true);
});
});
console.log('describe outer-b');
test('test 1', () => {
console.log('test for describe outer');
expect(true).toEqual(true);
});
describe('describe inner 2', () => {
console.log('describe inner 2');
test('test for describe inner 2', () => {
console.log('test for describe inner 2');
expect(false).toEqual(false);
});
});
console.log('describe outer-c');
});
// describe outer-a
// describe inner 1
// describe outer-b
// describe inner 2
// describe outer-c
// test for describe inner 1
// test for describe outer
// test for describe inner 2
- 위의 코드를 보면 테스트가 실행되기전에 순차적으로 describe안에있는 console.log들이 실행되는것을 확인할 수 있다.
일반적인 조언
test.only('this will be the only test that runs', () => {
expect(true).toBe(false);
});
test('this test will not run', () => {
expect('A').toBe('A');
});
- Test.only를 통해 테스트를 딱 한번만 실행되게 할 수 있다.
Written on March 14, 2020