Jest 비동기 코드

Bob C·2021년 11월 17일
0

Testing 비동기 코드

자바스크립트 코드는 비동기로 돌아갈때가 많습니다. 만약 당신이 비동기로 작동하는 코드를 작성하고 싶다면 Jest는 다음 테스트로 넘어가기 전에 테스트하는 코드가 언제 끝나는지 알아야 합니다.

Callbacks

가장 흔한 비동기 코드 패턴은 콜백입니다.

예를 들어 fetchData(callback) 함수를 부른다면 데이터를 페치하고 callback(data)를 끝날때 호출한다면 당신은 콜백 함수가 'peanut butter'를 반환하는지 알고 싶습니다.

초기값으로 Jest 테스트는 실행이 마지막에 도달하면 테스트를 끝냅니다. 이것은 테스트가 의도한대로 작동하지 않을 수도 있는 것을 의미합니다.

// Don't do this!
test('the data is peanut butter', () => {
  function callback(data) {
    expect(data).toBe('peanut butter');
  }
  
  fetchData(callback);
});

fetchData가 완료되면 콜백을 호출하기 전에 테스트가 끝납니다. 이것을 해결하기 위한 대체방안이 있습니다. 매개변수가 비어있는 함수를 호출하는 대신, done이라고 불리는 하나의 매개변수를 가지는 함수를 호출하면 Jest는 done 콜백이 호출되기 전까지 테스트를 끝내지 않고 기다립니다.

test('the data is peanut butter', done => {
  function callback(data) {
    try {
      expect(data).toBe('peanut butter');
      done();
    } catch (error) {
      done(error);
    }
  }
  
  fetchData(callback);
});

done이 호출되지 않으면 의도대로 테스트는 실패하게 됩니다.
만약 expect 구문이 실패하면 에러를 발생시키고 done은 호출되지 않습니다. 만약에 우리가 왜 실패했는지 알고 싶다면 expect를 try로 감싸고 에러 메시지를 캐치블록으로 넘겨주면 됩니다. 그렇지 않으면 우리는 불확실한 타임아웃 에러를 발생시키고 expect(data)가 받은 어떤 값도 출력하지 않습니다.

Promises

당신의 코드가 프로미스를 사용한다면 직접적인 비동기 코드를 테스트 하는 방법이 있습니다. 당신의 테스트에서 프로미스를 반환하십시오. Jest는 Promise가 Resolve되기를 기다릴 것입니다. 프로미스가 거절되면 테스트는 자동적으로 실패하게 됩니다.
예를 들어 fetchData는 callback을 사용하지 않고 프로미스를 반환합니다. 'peanut butter'를 반환하면 다음과 같이 테스트 할 수 있습니다.

test('the data is peanut butter', () => {
  return fetchData().then(data => {
    expect(data).toBe('peanut butter');
  });
});

프로미스를 반환하도록 하십시오. 반환하는 구문을 빠드린다면 fetchData에서 반환된 프로미스가 완료되기 전에 then이 이를 콜백을 실행할 기회를 얻기 전에 종료될 것입니다.

만약 당신이 프로미스가 거절되기를 기대한다면 .catch를 사용하세요. expect.assertions를 추가해서 특정한 숫자의 assertion이 호출되도록 하세요. 완료된 프로미스가 실패하지 않습니다.

test('the fetch fails with an error', () => {
  expect.assertions(1);
  return fetchData().catch(e => expect(e).toMatch('error'));
});

.resolves / .rejects#
You can also use the .resolves matcher in your expect statement, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail.

test('the data is peanut butter', () => {
return expect(fetchData()).resolves.toBe('peanut butter');
});
Be sure to return the assertion—if you omit this return statement, your test will complete before the promise returned from fetchData is resolved and then() has a chance to execute the callback.

If you expect a promise to be rejected, use the .rejects matcher. It works analogically to the .resolves matcher. If the promise is fulfilled, the test will automatically fail.

test('the fetch fails with an error', () => {
return expect(fetchData()).rejects.toMatch('error');
});
Async/Await#
Alternatively, you can use async and await in your tests. To write an async test, use the async keyword in front of the function passed to test. For example, the same fetchData scenario can be tested with:

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');
}
});
You can combine async and await with .resolves or .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');
});
In these cases, async and await are effectively syntactic sugar for the same logic as the promises example uses.

None of these forms is particularly superior to the others, and you can mix and match them across a codebase or even in a single file. It just depends on which style you feel makes your tests simpler.

profile
내일은 내일의 에러가 뜬다

0개의 댓글