Jest는 다른 방법으로 value를 test할 수 있도록 "matchers"를 사용한다.
value를 test하는 가장 쉬운 방법
test('two plus two is four', () => {
expect(2 + 2).toBe(4);
});
이 코드에서 expect(2+2)는 expectation object를 return한다. You typically won't do much with these expectation objects except call matchers on them. 이 코드에서, toBe(4)가 matcher이다. Jest가 실행될 때, 이건 모든 failing matchers를 추적하여 좋은 에러 메세지를 프린트해준다.
toBe는 정확하게 동일성을 test하기 위해 Object.is를 사용한다. 만약 object의 value를 check하고 싶다면, toEqual을 사용하세요.
test('object assignment', () => {
const data = {one: 1};
data['two'] = 2;
expect(data).toEqual({one: 1, two: 2});
});
toEqual은 재귀적으로 모든 object 또는 array를 check한다.
Tip: toEqual은 object keys with undefined properties, undefined array items, array sparseness, or object type mismatch를 무시한다. 대신 이 경우는 toStrictEqual을 사용한다.
not을 사용해서 matcher의 반대를 test할 수 있다.
test('adding positive numbers is not zero', () => {
for (let a = 1; a < 10; a++) {
for (let b = 1; b < 10; b++) {
expect(a + b).not.toBe(0);
}
}
});
test에서, 때로 undefined, null 그리고 false 사이에서 구별해야할 필요가 있다. 하지만 때로 이것을 다르게 다루고 싶지 않을 때도 있다. Jest는 원하는 것에 대해 명백하게 해주는 helpers를 포함한다.
For example:
test('null', () => {
const n = null;
expect(n).toBeNull();
expect(n).toBeDefined();
expect(n).not.toBeUndefined();
expect(n).not.toBeTruthy();
expect(n).toBeFalsy();
});
test('zero', () => {
const z = 0;
expect(z).not.toBeNull();
expect(z).toBeDefined();
expect(z).not.toBeUndefined();
expect(z).not.toBeTruthy();
expect(z).toBeFalsy();
});
You should use the matcher that most 정확하게 대응하는 to what you want your code to be doing.
숫자를 비교하는 대부분의 방법은 matcher equivalents를 가지고 있다.
test('two plus two', () => {
const value = 2 + 2;
expect(value).toBeGreaterThan(3);
expect(value).toBeGreaterThanOrEqual(3.5);
expect(value).toBeLessThan(5);
expect(value).toBeLessThanOrEqual(4.5);
// toBe and toEqual are equivalent for numbers
expect(value).toBe(4);
expect(value).toEqual(4);
});
소수점 equality를 위해 toEqual 대신 toBeCloseTo를 사용한다, 왜냐하면 작은 rounding error에 의존한 test를 원하지 않기 때문이다.
test('adding floating point numbers', () => {
const value = 0.1 + 0.2;
//expect(value).toBe(0.3); This won't work because of rounding error
expect(value).toBeCloseTo(0.3); // This works.
});
toMatch로 reqular expressiong(정규 표현식)에 대응하는 string을 check할 수 있다.
test('there is no I in team', () => {
expect('team').not.toMatch(/I/);
});
test('but there is a "stop" in Christoph', () => {
expect('Christoph').toMatch(/stop/);
});
만약 array 또는 iterable이 particular item을 포함한다면 toContain을 check할 수 있다.
const shoppingList = [
'diapers',
'kleenex',
'trash bags',
'paper towels',
'milk',
];
test('the shopping list has milk on it', () => {
expect(shoppingList).toContain('milk');
expect(new Set(shoppingList)).toContain('milk');
});
특정 function이 call 됐을 때, error를 throw를 하는지 아닌지 test 하고 싶다면 toThrow를 사용한다.
function compileAndroidCode() {
throw new Error('you are using the wrong JDK!');
}
test('compiling android goes as expected', () => {
expect(() => compileAndroidCode()).toThrow();
expect(() => compileAndroidCode()).toThrow(Error);
// You can also use a string that must be contained in the error message or a regexp
expect(() => compileAndroidCode()).toThrow('you are using the wrong JDK');
expect(() => compileAndroidCode()).toThrow(/JDK/);
// Or you can match an exact error message using a regexp like below
expect(() => compileAndroidCode()).toThrow(/^you are using the wrong JDK$/); // Test fails
expect(() => compileAndroidCode()).toThrow(/^you are using the wrong JDK!$/); // Test pass
});
Tip: exception을 throw 하는 function은 wrapping function 내에서 호출되어야 한다. 그렇지 않으면 toThrow assertion이 fail 하게 된다.
자바 스크립트에서 코드를 asynchronously하게 run하는 것이 일반적이다. 비동기적으로 실행되는 코드를 가지고 있을 때, Jest는 다른 test로 이동하기 전에, 테스트 중인 코드가 언제 완료되었는지 알 필요가 있다. 이를 다루기 위해 Jest는 여러 방법이 있다.
test로부터 Promise를 return한다. 그리고 Jest는 해결하기 위해 promise를 기다릴 것이다. 만약 promise가 거부된다면, test는 fail된다.
예를 들어, fetchData가 string 'peanut butter'를 해결하기위해 가정된 promise를 return 한다고 얘기해보자.
우리는 이렇게 테스트 할 수 있다.
test('the data is peanut butter', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
대안적으로, 테스트에서 async와 await을 이용할 수 있다. async test를 작성하기 위해, test로 passed되는 fuction의 앞에 async 키워드를 사용한다. 예를 들어, 같은 fetchData 시나리오가 이렇게 test 될 수 있다.
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');
}
});
async와 await을 .resolves 또는 .rejects로 결합할 수 있다.
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.toMatch('error');
});
이 경우, async와 await은 promise 예제에서 사용하는 것과 동일한 논리에 대한 효과적인 구문입니다.
주의: promise를 return(또는 await)하는 것을 확실히 해야한다. 만약 return 또는 await 상태를 제출할 때, test는 fetchData로부터 returnn된 promise가 resolves or rejects 되기 전에 완료된다.
민약 promise가 reject될 것이라고 예상한다면, .catch 메소드를 사용한다. assertion의 특정한 수가 호출되는지 입증하기 위해 expect.assertions를 추가해야한다. 그렇지 않으면 이행된 promise가 test에서 실패되지 않을 것이다.
test('the fetch fails with an error', () => {
expect.assertions(1);
return fetchData().catch(e => expect(e).toMatch('error'));
});
만약 promises를 사용하지 않는다면, callbacks를 사용할 수 있다. 예를 들어, promise를 리턴하는 대신 callback을 예상하는 fetchData를 얘기해보자. 완료될 때, 몇 데이터를 fetch하고 callback(null, data)를 호출한다. 리턴된 data가 string 'peanut butter'인지 테스트 하고 싶다.
기본적으로, Jest는 그 실행의 끝에 도달하면 일단 완료된다. 이것은 아래의 test는 의도된 대로 움직이지 않을 것이라는 의미이다.
// Don't do this!
test('the data is peanut butter', () => {
function callback(error, data) {
if (error) {
throw error;
}
expect(data).toBe('peanut butter');
}
fetchData(callback);
});
문제는 test가 fetchData가 되자마자 callback을 호출하기 전에 완료된 것입니다.
다음은 이걸 고친 test의 대안 형식이다. 빈 argument가 있는 함수에 test를 넣는 대신 done이라는 단일 인수를 사용한다.
Jest는 test가 끝나기 전에 done callback이 호출될 때까지 기다릴 것이다.
test('the data is peanut butter', done => {
function callback(error, data) {
if (error) {
done(error);
return;
}
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}
fetchData(callback);
});
만약 done()이 호출되지 않는다면, 테스트는 실패할 것이다. (타임아웃 에러와 함께), 이것이 원하는 바이다.
만약 expect 상태가 실패한다면, 이것은 error를 throw하고 done()이 호출되지 않을 것이다.
만약 test log에서 왜 실패됐는지 보고 싶다면, try block에 expect를 싸야 하고, catch block의 error를 done으로 보내줘야 한다. 그러지 않는다면, 어떤 값이 expect(data)에 의해 받아졌는지 보여지지 않는 불투명한 timeout error와 함께 끝나게 된다.
주의: 만약 같은 test function이 done() callback으로 pass되고 promise를 return 한다면 Jest는 error를 throw 할 것이다. 이것은 테스트에서 memory 누수를 피하기 위해 예방으로서 끝난다.
expect 상태에서 .resolves matcher를 사용할 수 있다, 그리고 Jestsms reslove 하기 위해 그 promise를 기다릴 것이다. 만약 promise가 reject된다면, 테스트는 자동적으로 fail 될 것이다.
test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});
ㅅ
assertion을 반드시 반환하세요, - 만약 이 return statement를 생략한다면, fetchData에서 반환돤 promise가 해결되기 전에 테스트가 완료되고 then()이 콜백을 실행할 기회가 있습니다.
만약 promise가 reject된다고 expect한다면, .rejects matcher를 사용한다. 이것은 analogically하게 .resolves matcher로 작동한다. 만약 promise가 이행된다면, test는 자동적으로 fail이다.
test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});
이들 형태 중 어느 것도 다른 형태보다 특별히 우수하지는 않습니다, 그리고 이것들을 codebase 또는 single file를 가로질러 mix and match 할 수 있습니다. 이것은 느끼기에 어떤 스타일이 test를 간단하게 만드는지 느끼는 것에 달려있습니다.
자주 test를 작성하는 동안 test가 실행되기 전에 일어나야 할 몇몇 setup 작업들을 가지고 있고, 그리고 test가 실행된 후에 일어나야 할 finising work를 가집니다.
Jest는 이것을 다루기 위해 helper functions를 제공합니다.
만약 많은 test를 위해 반복적으로 해야할 몇 작업들을 가지고 있다면, beforeEach와 afterEach hook들을 사용할 수 있다.
예를 들어, 여러 테스트가 도시 데이터베이스와 상호작용 한다고 가정해보면, 이 테스트들의 각각 전에 호출되야만 하는 initializeCityDatabase() 메소드를 가지고 있다. 그리고 이 각각의 테스트들이 끝날 때 호출되야만 하는 clearCityDatabase() 메소드를 가지고 있다.
beforeEach(() => {
initializeCityDatabase();
});
afterEach(() => {
clearCityDatabase();
});
test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});
test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});
beforeEach와 afterEach는 test가 비동기 code를 다룰 수 있는 것과 같은 방식으로 비동기 code를 다룰 수 있다. 이것들은 done parameter를 가질 수 있고 promise를 return 할 수 있다. 예를 들어, 만약 initializeCityDatabase()가 database가 초기화될 때 resolve 되는 promise를 리턴한다면, 우리는 아래의 promise를 return 하고자 할 것이다.
beforeEach(() => {
return initializeCityDatabase();
});
몇몇 경우에서, you only need to setup once, at the beginning of a file. This can be especially bothersome when the setup is asynchronous, so you can't do it inline. Jest provides beforeAll and afterAll hooks to handle this situation.
For example, if both initializeCityDatabase() and clearCityDatabase() returned promises, and the city database could be reused between tests, we could change our test code to:
beforeAll(() => {
return initializeCityDatabase();
});
afterAll(() => {
return clearCityDatabase();
});
test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});
test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});
The top level before+ and after+ hooks apply to every test in a file. The hooks declared inside a describe block apply only to the tests within that describe block.
For example, let's say we had not just a city database, but also a food database. We could do different setup for different tests.
// 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 veal', () => {
expect(isValidCityFoodPair('Vienna', 'Wiener Schnitzel')).toBe(true);
});
test('San Juan <3 plantains', () => {
expect(isValidCityFoodPair('San Juan', 'Mofongo')).toBe(true);
});
});
Note that the top-level beforeEach is executed before the beforeEach inside the describe block. It may help to illustrate the order of execution of all hocks.
beforeAll(() => console.log('1 - beforeAll'));
afterAll(() => console.log('1 - afterAll'));
beforeEach(() => console.log('1 - beforeEach'));
afterEach(() => console.log('1 - afterEach'));
test('', () => console.log('1 - test'));
describe('Scoped / Nested block', () => {
beforeAll(() => console.log('2 - beforeAll'));
afterAll(() => console.log('2 - afterAll'));
beforeEach(() => console.log('2 - beforeEach'));
afterEach(() => console.log('2 - afterEach'));
test('', () => console.log('2 - test'));
});
// 1 - beforeAll
// 1 - beforeEach
// 1 - test
// 1 - afterEach
// 2 - beforeAll
// 1 - beforeEach
// 2 - beforeEach
// 2 - test
// 2 - afterEach
// 1 - afterEach
// 2 - afterAll
// 1 - afterAll
Jest executes all describe handlers in a test file before it executes any of the actual tests. This is another reason to do setup and teardown inside before and after handlers rather than inside the describe blocks.
Once the describe blocks are complete, by default Jest runs all the tests serially in the order they were encountred in the collection phase, waiting for each to finis and be tidied up before moving on.
Consider the following illustrative test file and output:
describe('describe outer', () => {
console.log('describe outer-a');
describe('describe inner 1', () => {
console.log('describe inner 1');
test('test 1', () => console.log('test 1'));
});
console.log('describe outer-b');
test('test 2', () => console.log('test 2'));
describe('describe inner 2', () => {
console.log('describe inner 2');
test('test 3', () => console.log('test 3'));
});
console.log('describe outer-c');
});
// describe outer-a
// describe inner 1
// describe outer-b
// describe inner 2
// describe outer-c
// test 1
// test 2
// test 3
Just like the describe and test blocks Jest calls the before and after hooks in the order of declaration. Note that the after* hooks of the enclosing scope are called first. For example, here is how you can set up and tea down resources which depend on each other:
beforeEach(() => console.log('connection setup'));
beforeEach(() => console.log('database setup'));
afterEach(() => console.log('database teardown'));
afterEach(() => console.log('connection teardown'));
test('test 1', () => console.log('test 1'));
describe('extra', () => {
beforeEach(() => console.log('extra database setup'));
afterEach(() => console.log('extra database teardown'));
test('test 2', () => console.log('test 2'));
});
// connection setup
// database setup
// test 1
// database teardown
// connection teardown
// connection setup
// database setup
// extra database setup
// test 2
// extra database teardown
// database teardown
// connection teardown
Note: