Jest | 비동기 코드 테스트

Kate Jung·2022년 3월 26일
0

Front-end Test

목록 보기
3/17
post-thumbnail

📌 Callback 패턴

🔹 test함수에 콜백 함수를 전달하지 않았을 경우

제대로 테스트 되지 않음.

◽ [통과]하는 코드

3초(3000ms) 이상 걸려야 하는데 바로 통과(1ms)되고 종료

  1. 3초 후에 이름을 알려주는 함수 작성 (fn.js)

    /* 
    getName 함수는 콜백 함수를 받아서 3초 후에 name을 
    콜백의 첫 번째 인수로 넘겨줌. */
    
    const fn = {
      add: (num1, num2) => num1 + num2,
      getName: (callback) => { // 👈
        const name = "Mike";
        setTimeout(() => {
          callback(name);
        }, 3000);
      },
    };
    
    module.exports = fn;
  2. 사용하는 코드 작성 (fn.test.js)

    /*
    getName함수 호출하고 콜백 함수 전달. 
    콜백은 name을 받아서 테스트 진행. */
    
    const fn = require("./fn");
    
    test("3초 후에 받아온 이름은 Mike", () => {
      function callback(name) {
        expect(name).toBe("Mike");
      }
      fn.getName(callback);
    });
  3. 테스트

    npm test

  4. 결과 확인

◽ [실패]하는 코드

테스트 하니, 바로 통과

  1. 실패하는 코드로 수정 (fn.test.js)

    const fn = require("./fn");
    
    test("3초 후에 받아온 이름은 Mike", () => {
      function callback(name) {
        expect(name).toBe("Tom"); // 👈 Tom으로 수정
      }
      fn.getName(callback);
    });
  2. 테스트(npm test) 후, 결과 확인

◽ 제대로 되지 않는 이유

  • 아마 가장 많이 사용되는 비동기 패턴이 이렇게 콜백을 사용하는 방식일 것.

  • but, Jest는 실행이 끝에 도달하게 되면 그대로 끝남.

    → 그래서 예정되어 있던 콜백함수가 실행되지 않는 것.

    • fn.test.js
      const fn = require("./fn");
      
      test("3초 후에 받아온 이름은 Mike", () => {
        function callback(name) {
          expect(name).toBe("Tom");
        }
        fn.getName(callback); // 👈 기다리지 않고 그냥 여기서 그냥 끝내는 것.
      });
  • 해결 방법

    test함수에 콜백 함수(ex. done)를 전달

🔹 test함수에 콜백 함수를 전달

모두 제대로 테스트 됨.
콜백 함수가 호출되기 전까지 Jest는 test를 끝내지 않고 기다림.

◽ [실패]하는 코드

  1. 콜백 함수(done) 전달 (fn.test.js)

    const fn = require("./fn");
    
    test("3초 후에 받아온 이름은 Mike", (done) => { // 👈
      function callback(name) {
        expect(name).toBe("Tom");
        done(); // 👈 callback이 실행되고 여기에 done 호출
      }
      fn.getName(callback);
    });
  2. 테스트(npm test)

    • 실패 (3초이상 걸림)

      • 이유 정확히 알려줌

        Tom을 기대했는데 Mike 나옴.

◽ [통과]하는 코드

  1. fn.test.js 의 test의 toBe를 다시 Mike로 바꾸기

  2. 테스트(npm test) 결과 확인

    3초 정도가 걸리고 통과

◽ 콜백 함수를 호출하지 않을 경우

테스트 실패할 것

  • fn.test.js
    const fn = require("./fn");
    
    test("3초 후에 받아온 이름은 Mike", (done) => {
      function callback(name) {
        expect(name).toBe("Mike");
        // done(); // 👈
      }
      fn.getName(callback);
    });
    
    /*
    - 결과:
    한참 걸리다가 실패.
    살펴 보면 Timeout임. 5초 동안 timeout이 있고 그동안 응답이 없으면 실패로 간주. */

◽ API의 에러를 감지하고 싶다면

try catch문으로 감싸주기

  • 예시 코드
    1. 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();
          }
        }
        fn.getName(callback);
      });
    2. 함수에서 에러를 발생 시킴 (fn.js)

      const fn = {
        add: (num1, num2) => num1 + num2,
        getName: (callback) => {
          const name = "Mike";
          setTimeout(() => {
            throw new Error("서버 에러.."); // 👈
          }, 3000);
        },
      };
      
      module.exports = fn;
    3. 테스트(npm test) 후, 결과 확인

      에러 잘 찍힘

📌 Promise

🔹 사용법 (Callback 과 비교)

  • 더 명확 & 간결
  • 콜백 함수 안 넘겨도 됨 & return 필요
  • 콜백 함수 안 넘겨도 되는 이유

    Promisereturn해주면 Jestresolve될 때까지 기다림.

◽ 예시

  1. 함수 제작 (fn.js)

    나이를 받아오는 함수 제작 & 3초 후에 resolve

    const fn = {
    
      (...생략)
    
      getAge: () => {
        const age = 30;
        return new Promise((res, rej)=>{
          setTimeout(()=>{
            res(age)
          }, 3000)
        })
      }
    };
    
    module.exports = fn;
  2. 테스트 코드_return 無 (fn.test.js)

    문제 有

    const fn = require("./fn");
    
    test("3초 후에 받아온 나이는 30", () => {
      fn.getAge().then((age) => { // 👈 return 無
        expect(age).toBe(30);
      });
    });
    • 테스트 결과

      통과. 그러나 빠르게 결과가 나옴. → 이상함.

    • .toBe 의 숫자를 바꿔도 (.toBe(31)) 통과

  3. 테스트 코드 문제 해결_return 추가 (fn.test.js)

    const fn = require("./fn");
    
    test("3초 후에 받아온 나이는 30", () => {
      return fn.getAge().then((age) => { // 👈
        expect(age).toBe(31);
      });
    });
    • 테스트 결과

      • 3초가 걸림 & 31이라서 실패 → 제대로 동작함.

        (return 없으면 아까처럼 그냥 종료)

      • .toBe(30); 수정 시

        3초가 걸림 & 통과 → 제대로 동작함.

🔹 matcher 활용

간단하게 작성하고 싶을 때

◽ resolves 예시

  • fn.test.js
    const fn = require("./fn");
    
    test("3초 후에 받아온 나이는 30", () => {
      return expect(fn.getAge()).resolves.toBe(30); // 👈
    });
    
    // 테스트 결과: 통과

◽ reject 예시

  1. [불통]일 경우

    • fn.js
      const fn = {
        (...생략)
        getAge: () => {
          const age = 30;
          return new Promise((res, rej) => {
            setTimeout(() => {
              rej("error"); // 👈 이 부분을 바꿔주면 됨
            }, 3000);
          });
        },
      };
      
      module.exports = fn;
    • fn.test.js
      const fn = require("./fn");
      
      test("3초 후에 받아온 나이는 30", () => {
        return expect(fn.getAge()).rejects.toBe(30); // 👈 rejects로 수정
      });
      
      // 테스트 결과: 실패 -> 제대로 동작
  2. [통과]일 경우

    에러인지 확인하기 위해서 toMatch('error')로 수정

    • fn.test.js
      const fn = require("./fn");
      
      test("3초 후에 에러가 납니다.", () => { // 👈 설명 수정
        return expect(fn.getAge()).rejects.toMatch("error"); // 👈
      });
      
      // 테스트 결과: 통과함. (3초 후, 에러 발생) -> 제대로 동작

📌 async, await

🔹 사용법

async, await 사용 방식과 동일.

◽ [성공]할 경우

  1. 함수 (fn.js)

    const fn = {
      (...생략)
      getAge: () => {
        const age = 30;
        return new Promise((res, rej) => {
          setTimeout(() => {
            res(age); // 👈
          }, 3000);
        });
      },
    };
    
    module.exports = fn;
  2. 테스트 코드 (fn.test.js)

    const fn = require("./fn");
    
    test("3초 후 나이 30", async () => {
      const age = await fn.getAge();
      expect(age).toBe(30);
    });
    
    // 테스트 결과:  통과 -> 제대로 동작

◽ [실패]할 경우

  1. fn.test.js 의 숫자 바꿈. (.toBe(31);)

  2. 테스트 후, 결과 확인

    3초 걸림 & 실패

  3. 다시 성공케이스로 수정

    30으로 수정 (.toBe(30);)

  4. 테스트 후, 결과 확인

    성공

🔹 matcher 활용

return 대신 await

  • fn.test.js
    const fn = require("./fn");
    
    test("3초 후 나이 30", async () => { // 👈 async
      await expect(fn.getAge()).resolves.toBe(30); // 👈 await
    });
    
    // 테스트 결과: 통과 -> 제대로 동작

참고

  • 코딩앙마 강좌_Jest
profile
복습 목적 블로그 입니다.

0개의 댓글