[테스트] jest를 이용한 테스트 방법 정리!

이나원·2024년 4월 24일
0

개발일지

목록 보기
22/22

💡 jest ?

  • 자바스크립트 테스트용 프레임워크로, 검증을 실행하는 matcher와 모킹에 필요한 api를 제공해주어 많은 사람들이 사용하고 있다.
  • Node.js 환경에서 테스트를 실행하기 때문에 브라우저 환경에서의 DOM 조작 같은 검증은 불가능하다는 특징을 가지고 있지만,
  • jest가 제공하는 jsdom 환경을 사용하면 브라우저와 동일하게 DOM 에 대한 테스트 검증이 가능해진다.

jest 설치

npm install -D jest

커스텀 명령어 작성

scripts : {
  "test:jest" : "jest"
}

테스트 코드 작성

  • it() : it() 함수 내에는, 테스트에 대한 설명 및 jest 테스트 코드를 함수 내에 작성한다.

    it('2*2 = 4 이다.', () => {
        expect(2*2).toBe(4);
    });
  • test() : it() 함수 대신에 대체 가능한 함수, test() 함수는 it() 함수의 별칭으로, 기능적으로 동일한 역할이다. (it은 should 같은 느낌 test는 if 같은 느낌)

    test('2*2 = 4 이다.', () => {
        expect(2*2).toBe(4);
    });
  • describe() : 여러 테스트를 그룹화하는 역할, 동일한 범주의 테스트들을 그룹화해 블록을 생성한다.

    describe('두 수를 곱하면' , () => {
        test('4이다', () => {
            expect(2*2).toBe(4);
        });
    
        test('6이다', () => {
            expect(2*3).toBe(6);
        });
    })

테스트 실행

npm run test:jest

  • 테스트를 실행한 결과를 보면, 테스트 작성 시 설명을 적은 것이 그대로 노출되는 것을 볼 수 있다.

  • 즉, 내부 테스트 코드를 보지 않고도 어떤 검증을 위한 것인지가 명확히 파악되도록 테스트 설명을 상세히 알기 쉽게 적는 것이 중요하다.

matcher

💡 matcher ?

  • 값이 특정 조건 만족하는지 검증할 수 있는 집합을 말한다.
  • jest는 expect 함수를 이용해 matcher 실행하며 이외에도 여러 종류의 matcher 제공한다.
  • matcher는 굉장히 이름이 명시적으로 되어있어서 코드 가독성 측면에서 좋다.
  • toBe() : 원시 값의 일치 여부 확인, 객체 참조 확인

    		// toBe.test.js
    test("2*2 = 4 이다.", () => {
      expect(2 * 2).toBe(4);
    });
  • toEqual() : 값의 일치여부 확인, 객체의 모든 프로퍼티 값 재귀적 비교 후 일치 확인

    ```js
    // toEqual.test.js
    const info_1 = {
      name: "nawon",
    };
    
    const info_2 = {
      name: "nawon",
    };
    
    test("두 사람의 정보가 동일하다.", () => {
      expect(info_1).toEqual(info_2);
    });
    ```

  • toHaveBeenCalled() : spy를 사용해 특정 함수가 정상적으로 호출되었는지 검증한다.

    • spy : 함수의 호출 횟수, 호출 인자를 기록하며, jest에서는 spyOn 함수를 이용해 spy 생성이 가능하다.
    describe("spy", () => {
      let foo;
      let bar;
    
      beforeEach(() => {
        foo = {
          setBar(value) {
            bar = value;
          },
        };
        jest.spyOn(foo, "setBar");
    
        foo.setBar(999);
      });
    
      it("호출 여부 검증", () => {
        expect(foo.setBar).toHaveBeenCalled();
      });
    })

  • toHaveBeenCalledWith() : toHaveBeenCalled()과 유사하며, 함수에 어떤 인자가 넘어갔는지까지 검사해준다.

    expect(foo.setBar).toHaveBeenCalledWith(999);
  • toHaveBeenCalledTimes() : toHaveBeenCalled()과 유사하며, 함수가 몇 번 호출됐는지 검사해준다.

    expect(foo.setBar).toHaveBeenCalledTimes(1);
  • toHaveProperty() : 객체에 특정 프로퍼티 값이 들어있는지 확인하며, 중첩된 프로퍼티 값도 확인 가능하다.

    const info = {
      name: "nawon",
      age: "25",
      job: {
        name: "frontend developer",
        location: "apgujeong rodeo",
      },
    };
    
    test("이 사람은 다음 옵션을 가지고 있다.", () => {
      expect(info).toHaveProperty("name");
      expect(info).toHaveProperty("job.name", "frontend developer");
    });

  • toMatch() : 문자열이 정규식에 대응되는지 확인해준다.

      ```js
      test("test 문자열이 포함되어 있다.", () => {
          expect("test jest").toMatch("test");
          });
      ```

  • toMatchObject() : 객체 프로퍼티의 하위 집합이 특정 객체와 일치하는지 확인한다.

    const info = {
      name: "nawon",
      age: "25",
      job: {
        name: "frontend developer",
        location: "apgujeong rodeo",
      },
    };
    
    test("이 사람은 다음 옵션을 가지고 있다.", () => {
      const options = {
        job: {
          name: "frontend developer",
        },
      };
    
      expect(info).toMatchObject(options);
    });
  • toThrow() : 특정 상황에서 에러가 발생하는지 확인한다.

    test("TypeError 에러가 발생한다.", () => {
      const errorFunc = () => {
        throw new TypeError();
      };
      expect(errorFunc).toMatch(TypeError);
    });

    Setup & Teardown

  • jest는 전/후처리 작업을 위해 before*(Setup) 과 after*(Teardown) 을 제공한다.

  • 해당 함수들은 테스트 실행 전과 후에 공통적인 작업이 필요한 경우 사용한다.

  • beforeEach : 모든 테스트가 실행되기 전, 해당 함수 내에 작성한 함수를 먼저 실행한다.

    • 테스트에 사용할 공통 설정 적용 시 유용하며, 항상 먼저 실행되어야 하는 코드를 작성해주면 중복 코드 제거에 효과적이다.

      beforeEach(() => {
        container = document.createElement("div");
        const text = "text";
      
        memoItem = new MemoItem(container, { text });
      });
  • afterEach : 테스트 실행이 완료될 때 마다 해당 함수에 작성한 함수가 실행한다.

    • 테스트 결과로 생성된 상태를 초기화할 때 유용하며, 테스트 실행 중에 DOM 요소나 다른 테스트에도 공유되는 상태 값을 변경한 경우 해당 함수를 통해 다시 초기화 할 수 있다.

      afterEach(() => {
        cleanState();
        el.remove();
      });
  • beforeAll : 테스트 실행 전에 호출되는 것은 동일하지만, 테스트 실행 전 한번만 호출되며, beforeEach와 같이 있으면 beforeAll이 먼저 실행된다.

    beforeAll(() => console.log("beforeAll"));
    beforeEach(() => console.log("beforeEach"));
    test("before 순서 알아보기", () => {
      console.log("test");
    });
  • afterAll : 테스트 실행 후에 호출되는 것은 동일하지만, 테스트 실행이 모두 완료된 후 한번만 호출되며, afterEach와 같이 있으면 afterEach가 먼저 실행된다.

    afterAll(() => console.log("afterAll"));
    afterEach(() => console.log("afterEach"));
    test("after 순서 알아보기", () => {
      console.log("test");
    });

테스트 더블

  • 프론트엔드 테스트에서 외부 인터페이스나 api를 호출해 데이터 받아오는 처리에 필요한 개념들을 통틀어 말한다.

  • 더미 : 객채/함수가 필요하지만 실제 기능까지는 필요없는 경우 사용한다.

    const dummyFunc = () => {};
    
    function createInfo(callback) {
      const info = {
        // ...
      };
    
      callback();
    }
    
    it("info 객체를 올바르게 생성한다.", () => {
      createInfo(dummyFunc);
    });
  • 스텁 : 객체가 실제로 동작하는 것처럼 만들어 놓은 것이다.

    const stub = jest.fn((value) => value + 1);
    
    it("스텁 테스트", () => {
      console.log(stub(1)); // 2
    });
    • spy 나 mock을 이용해 특정 객체 일부나 전체를 스텁으로 만들어 대체 가능

      import oneModule from './module';
      
      // 모듈의 일부 메서드 스텁으로 대체
      jest.spyOn(oneModule, 'getNum').mockImplementation((value) => value + 1);
      
      // 모듈 전체를 스텁으로 대체
      jest.mock('./module', () => {
          return {
              getNum: jest.fn((value) => value + 1);
          }
      })
      
      it("스텁 테스트", () => {
      console.log(oneModule.getNum(1)); // 2
      });
  • 스파이 : 스텁과 유사, 객체가 어떤 인자를 가지고 있고, 몇 번 호출되었는지 알려준다.

    import obj from "./obj";
    
    it("스파이 테스트", () => {
      jest.spyOn(obj, "getNum"); // 스파이 생성
    
      expect(obj.getNum).toHaveBeenCalled();
    });
    • 스파이는 자신이 호출되었을 때의 상황을 기록해줘서 콜백 함수 (이벤트 리스너 같은) 호출 여부 검증에 유용하다.

    • 실제 객체/모듈의 구현을 그대로 유지한 채 호출 정보를 기록한다. (스텁과의 차이점)

      스텁스파이
      일부 제한된 구현만 대체하는 경우기존 모듈 동작을 사용하면서 호출 여부 판단, 일부 동작만 대체하는 경우
  • 목 (Mock) : 실제 객체와 동일한 기능을 할 수 있도록 만들어진 모의 객체이다.

    import mock from "jest-mock-axios";
    
    it("mock 테스트", () => {
      let catchFunc = jest.fn();
      let thenFunc = jest.fn();
    
      addUser("nawon").then(thenFunc).catch(catchFunc);
    
      expect(mock.post).toHaveBeenCalledWith("/addUser/", { data: "nawon" });
      expect(thenFunc).toHaveBeenCalled();
      expect(catchFunc).toHaveBeenCalled();
    });
profile
프론트엔드 개발자로 재직 하면서 겪은 개발 과정을 기록하는 곳입니다 🙌

0개의 댓글