자바스크립트에서 코드가 비동기로 실행되는 일이 많이 일어난다. 비동기로 실행되는 코드가 있는 경우, Jset는 다른 테스트로 옮겨가기 이전에, 테스트 중인 코드가 언제 완료되었는지 알아야 할 필요가 있다. Jest는 이를 처리하기 위해 몇가지 방법이 있다.
콜백 함수 테스트
가장 일반적인 비동기 패턴이다.
// fn.js
const fn = {
add: (num1, num2) => num1 + num2,
getName: (callback) => {
const name = "Mike";
setTimeout(() => {
callback(name);
}, 3000);
},
}
module.exports = fn;
fn파일에 getName을 생성하고, 테스트를 진행 한다.
// fn.test.js
const fn = require("./fn");
test("3초후에 받아온 이름은 Mike", () => {
function callback(name) {
expect(name).toBe("Mike");
}
fn.getName(callback);
});
태스트는 통과한다. 그런데 이상한 점이 발견 되는데, setTime함수를 이용해서 3초후에 실행하게 만들었는데 실행된 시간은 3 ms이다.
PASS ./fn.test.js
✓ 3초후에 받아온 이름은 Mike (3 ms)
Jest는 기본적으로 실행이 끝에 도달하게 되면 테스트가 종료된다. 이는 테스트가 의도한 대로 동작하지 않을 것이다.
즉 콜백을 호출하기도 전에 fn.getName이 끝나자마자 종료된다.
이 문제를 해결하기 위해 test함수에 done이라는 인자를 넣어 사용하면 된다.
done이 호출되기 전까지 Jest는 테스트를 끝내지 않고 기다리게 된다.
// fn.test.js
const fn = require("./fn");
test("3초후에 받아온 이름은 Mike", () => {
function callback(name) {
expect(name).toBe("Mike");
done()
}
fn.getName(callback);
});
done()을 넣어주고 다시 테스트를 돌려보면, 3초후에 정확히 통과 되는 것을 볼 수 있다.
PASS ./fn.test.js
✓ 3초후에 받아온 이름은 Mike (3013 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.998 s
만약 API의 에러를 감지하고 싶다면, try, catch 문으로 감싸준다.
// fn.test.js
const fn = require("./fn");
test("3초후에 받아온 이름은 Mike", (done) => {
function callback(name) {
try {
expect(name).toBe("Mike");
done();
} catch (error) {
done(error);
}
}
fn.getName(callback);
}); //fail
Promise 테스트
Promise를 사용하는 것이 더 명확하고 간결하다.
// fn.js
const fn = {
getAge: () => {
const age = 30;
return new Promise((res, rej) => {
setTimeout(() => {
res(age);
}, 3000);
});
},
}
module.exports = fn;
fn파일에 getAge를 만든다. Promise를 리턴해주면 Jest는 resolve될때 까지 기다려 준다. 테스트를 돌려보면,
const fn = require("./fn");
test("3초 후에 받아온 나이는 30", () => {
fn.getAge().then((age) => {
expect(age).toBe(30);
});
});
통과 되는 것을 볼 수 있다. 그런데 빠르게 통과된다.
PASS ./fn.test.js
✓ 3초 후에 받아온 나이는 30 (2 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.477 s, estimated 6 s
Promise를 사용할때에는 아래와 같이 return문을 추가해야한다. return을 작성하지 않으면 Promise객체의 then()메서드가 실행되지 않고 바로 종료된다.
return문을 작성하고 다시 테스트를 실행해 보면,
const fn = require("./fn");
test("3초 후에 받아온 나이는 30", () => {
return fn.getAge().then((age) => {
expect(age).toBe(30);
});
});
3초에 걸쳐 테스트가 통과하는것을 볼 수 있다.
PASS ./fn.test.js
✓ 3초 후에 받아온 나이는 30 (3007 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.472 s
Ran all test suites.
더 간단하게 사용하는 방법은 Matcher를 사용하는 것이다.
const fn = require("./fn");
//resolves, rejects (Matcher 사용)
test("3초 후에 받아온 나이는 30", () => {
// return fn.getAge().then((age) => {
// expect(age).toBe(30);
// });
return expect(fn.getAge()).resolves.toBe(30);
});
// passed
rejects로 에러 발생
// fn.js
const fn = {
getAge: () => {
const age = 30;
return new Promise((res, rej) => {
setTimeout(() => {
rej("error");
}, 3000);
});
},
}
module.exports = fn;
3초후에 에러를 발생하는 코드를 만들고,
const fn = require("./fn");
//resolves, rejects (Matcher 사용)
test("3초 후에 받아온 나이는 30", () => {
// return fn.getAge().then((age) => {
// expect(age).toBe(30);
// });
return expect(fn.getAge()).rejects.toMatch("error");
});
// passed
toMatch()로 에러가 맞는지 확인하고 테스트는 통과하게 된다.
Async / Await 테스트
Async / Await는 일반적으로 사용하는 법과 같다.
const fn = require("./fn");
test("3초 후에 받아온 나이는 30", async () => {
const age = await fn.getAge();
expect(age).toBe(30);
});
함수 맨 앞에 async를 추가하고, Promise를 리턴하는 함수 앞에 await를 붙여주며 사용한다.
마찬가지로 Matcher를 사용할 수 있다.
const fn = require("./fn");
test("3초 후에 받아온 나이는 30", async () => {
await expect(fn.getAge()).resolves.toBe(30);
}); //passed
끝으로
Jest에서 비동기를 테스트하는 코드를 작성해보았다. 다음으로는 Jest로 전/후 처리에 대해 공부해보자.
참고자료
'Testing' 카테고리의 다른 글
[Jest #4] 리액트 컴포넌트 + 스냅샷 테스트 (0) | 2023.07.22 |
---|---|
[Jest #3] 테스트 전/후 작업 (0) | 2023.07.20 |
[Jest #1] Jest로 기본적인 테스트 작성법 (0) | 2023.07.18 |
댓글