본문 바로가기
Testing

[Jest #2] Jest로 비동기 코드 테스트

by 쾌횽 2023. 7. 19.
반응형

자바스크립트에서 코드가 비동기로 실행되는 일이 많이 일어난다. 비동기로 실행되는 코드가 있는 경우, 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로 전/후 처리에 대해 공부해보자.

 


참고자료

반응형

댓글