ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Jest] 기본 활용법
    React/jest 2023. 8. 3. 14:38

    설치 및 세팅

    설치

    npm install jest --save-dev
    • 개발환경에서만 테스트를 사용을 하니 개발환경에 설치

    세팅

    • npm test를 치면 테스트가 가능
    • ~.test.js 에 해당하는 파일을 전부 검사

    참고 : https://www.youtube.com/watch?v=g4MdUjxA-S4

    Matcher

    toBe

    test("1은 1이야", () => {
      expect(1).toBe(1);
    });
    • expect : test할 값
    • toBe : 예상하는 값
    test("1은 2가 아니야", () => {
      expect(1).not.toBe(2);
    });
    • 부정값을 예상하는 경우
    test("0.1+0.2 = 0.3??", () => {
      expect(fn.add(0.1, 0.2)).toBeCloseTo(0.3);
    });

    • javascript 소수 계산시 무한소수가 되기 때문에 toBeCloseTo사용

    toBeCloseTo를 사용해야 하는 이유 -> 부동소수점

    부동소수점? 
    부동소수점이란 실수를 표현할 때 소수점의 위치를 고정하지 않는 것을 말한다. 
    부동 소수점(floating point) 방식은 소수점 (point) 이 둥둥 떠다닌다 (floating) 라는 의미로, 표현할 수 있는 값을 범위를 최대한 넓혀 오차를 줄이자는 시도에서 탄생한 녀석이다.
    부동 소수점은 고정 소수점 방식과는 달리 메모리를 가수부(23bit)와 지수부(8bit)로 나눈다.
     
    기수부 에는 실제 실수 데이터 비트들이 들어가고, 지수부에는 소수점의 위치를 가리키는 제곱승 이 들어간다고 보면 된다.
    뭔가 직관적이지 않아 오히려 번거롭게 보일수도 있곘지만, 이런식으로 실수를 표현하는 이유는 큰 범위의 값을 표현하기 위해 서이다.
     자세히 알아보기 

    그외

    • toBeNull
    • toBeDefined
    • toBeTruty
    • toBeFalsy
    • toBeGreaterThan
    • toBeGeaterThanOrEqual
    • toBeLessThan
    • toBeLessThanOrEqual

    toEqual

    //fn.js
    
    const fn ={
      makeUser: (name, age, gender) => ({ name, age, gender: undefined }),
    }
    • user를 만드는 함수를 정의
    //fn.test.js
    
    test("toBe : name and age to object", () => {
      expect(fn.makeUser("Mike", 30)).toBe({
        name: "Mike",
        age: 30,
      });
    });

    • 에러가 나오는 이유는 toBe는 같은 객체인지 확인
    • 위에는 내용이 같을 뿐 다른 객체
    test("ToEqual : name and age  to object", () => {
      expect(fn.makeUser("Mike", 30)).toEqual({
        name: "Mike",
        age: 30,
      });
    });
    • 내용이 같은지 확인을 할 때는 toEqual사용
    //fn.test.js
    
    test("toStrictEqual : name and age  to object", () => {
      expect(fn.makeUser("Mike", 30)).toStrictEqual({
        name: "Mike",
        age: 30,
      });
    });

    • strictEqual로 할 시에는 gender가 undefined상태이기 때문에 테스트에 실패

    toMatch

    test("check the letter with RegEx ", () => {
      expect("hello world").toMatch(/h/);
    });
    • 정규표현식을 사용해서 문자열 체크가 가능

    toContain

    test("check the list ", () => {
      const user = "Mike";
      const userList = ["Tom", "Jane", "Kai"];
      expect(userList).toContain("Kai");
    });
    • 베열안에 요소 확인 가능

    toThrow

    //fn.js
    const fn = {
      throwErr: () => {
          throw new Error("xx");
        }
    };
    //fn.test.js
    
    test("check the err ", () => {
      expect(() => fn.throwErr()).toThrow("xx");
    });
    • 설정한 에러메시지대로 출력이 됐는지 확인 가능

    비동기 함수 결과

    callback

    //fn.js
    const fn = {
      getName: (callback) => {
        const name = "Mike";
        setTimeout(() => {
          callback(name);
        }, 3000);
      }
    }
    
    //fn.test.js
    test("async func ", (done) => {
      function callback(name) {
        expect(name).toBe("Mike");
        done();
      }
      fn.getName(callback);
    });
    • callback을 사용하는 경우
    • 이제는 거의 사용 x

    promise

    //fn.js
    
    const fn = {
      getAge: () => {
        const age = 30;
        return new Promise((res, rej) => {
          setTimeout(() => {
            res(age);
          }, 3000);
        });
      }
    }
    
    
    //fn.test.js
    //방법1
    test("async func ", () => {
      return fn.getAge().then((age) => {
        expect(age).toBe(30);
      });
    });
    
    //방법2
    test("async func ", () => {
      return expect(fn.getAge()).resolves.toBe(30);
    });

    • 테스트에서 return을 안걸면 비동기작업이 이루어지지 않아서 테스트를 통과해버리는 상황이 벌어짐 - 소요시간이 1ms
    • return을 걸면 비동기처리가 잘 작동(settimeout 3 sec)하며 테스트

    async await

    test("check the promise ", async () => {
      const age = await fn.getAge();
      expect(age).toBe(30);
    });
    test("check the promise ", async () => {
      await expect(fn.getAge()).resolves.toBe(30);
    });
    • async await를 테스트 하는 방법

    테스트 전후 처리

    각 테스트 전후

    beforeEach, afterEach

    //각 테스트 전후 변수 초기화 
    let num = 0;
    
    beforeEach(() => {
      num = 0;
    });
    
    test(" 0 + 1  = 1? ", () => {
      num = fn.add(num, 1);
      expect(num).toBe(1);
    });
    
    test(" 0 + 2  = 2? ", () => {
      num = fn.add(num, 2);
      expect(num).toBe(2);
    });
    
    test(" 0 + 3  = 3? ", () => {
      num = fn.add(num, 3);
      expect(num).toBe(3);
    });
    

    전체 테스트 전후

    beforeAll, afterAll

    // 테스트 전 db connect , 테스트 후 db disconnect
    
    beforeAll(async ()=>{
    	user = await fn.connectUserDb()
    })
    
    afterAll(async ()=>{
    	user = await fn.disConnectUserDb()
    })
    
    

    describe 관련 테스트 하나로 묶기

    describe(" 숫자 체크  ", () => {
      test(" 1? ", () => {
        expect(1).toBe(1);
      });
    
      test(" 0? ", () => {
        expect(0).toBe(0);
      });
    });

    여러 테스트 중 하나만 테스트

    describe(" 숫자 체크  ", () => {
      test.only(" 1? ", () => {
        expect(1).toBe(1);
      });
    
      test(" 0? ", () => {
        expect(0).toBe(0);
      });
    });
    • test.only사용

    여러 테스트 중 특정 테스트 제외

    describe(" 숫자 체크  ", () => {
      test(" 1? ", () => {
        expect(1).toBe(1);
      });
    
      test.skip(" 0? ", () => {
        expect(0).toBe(0);
      });
    });
    • test.skip사용

    Mocking

    • 외부 함수에 의존하지 않고 테스트 하고자할 때
    • user db 에서 가져와야 하는 경우 등 - 외부 요인(네트워크)에 의해 영향 많이 받음
    const mockFn = jest.fn();
    
    mockFn();
    mockFn(1);
    
    test(" 0 + 3  = 3? ", () => {
      console.log(mockFn.mock.calls);
      let num = 0;
      num = fn.add(num, 3);
      expect(num).toBe(3);
    });
    
    test("함수는 2번 호출됩니다. ", () => {
      expect(mockFn.mock.calls.length).toBe(2);
    });
    
    test("2번째로 호출된 함수에 전달된 첫번째 인수슨 1입니다. ", () => {
      expect(mockFn.mock.calls[1][0]).toBe(1);
    });
    

    • 함수에서 mockFn.mock.calls을 찍어보면 위처림 [ [ ] , [1]]이 나온다.
    • 첫번째 mockFn()에서 받은 인자가 없어서 빈리스트
    • 첫번째 mockFn()에서 받은 인자가 1이라 리스트 안에 1
    • mockFn.mock.calls.length : 2번 호출 해서 2
    • mockFn.mock.calls[1][0] : 호출한 mock 중에 2번째 함수의 인자는 1
    // 별도의 함수 만들지 않아도 테스트 가능 - 1을 더하는 콜백함수 없이 테스트
    
    const mockFn = jest.fn();
    function forEachAdd1(arr) {
      arr.forEach((num) => {
        mockFn(num + 1);
      });
    }
    
    forEachAdd1([10, 20, 30]);
    
    test("함수는 3번 호출됩니다.", () => {
      expect(mockFn.mock.calls.length).toBe(3);
    });
    
    test("전달된 값은 11, 21, 31입니다. ", () => {
      expect(mockFn.mock.calls[0][0]).toBe(11);
      expect(mockFn.mock.calls[1][0]).toBe(21);
      expect(mockFn.mock.calls[2][0]).toBe(31);
    });
    

    • 별도의 파일에 함수를 만들지 않고 테스트 내부에서 함수를 정의해도 테스트 가능
    • loop를 3번 돌때마다 mock이 일어나서 length = 3
    • 각각에 인자는 배열의 인자 +1
    const mockFn = jest.fn((num) => num + 1);
    
    mockFn(10);
    mockFn(20);
    mockFn(30);
    
    test("10에서 1증가한 값이 반환 ", () => {
      expect(mockFn.mock.results[0].value).toBe(11);
    });
    
    test("20에서 1증가한 값이 반환 ", () => {
      expect(mockFn.mock.results[1].value).toBe(21);
    });
    test("20에서 1증가한 값이 반환 ", () => {
      expect(mockFn.mock.results[2].value).toBe(31);
    });
    
    • jest.fn()에 익명함수를 정의하는 방법도 있음

    비동기 함수 mock

    const mockFn = jest.fn();
    
    mockFn.mockResolvedValue({ name: "mike" });
    
    test("비동기 콜백함수로 받아온 결과는 mike ", () => {
      mockFn().then((res) => {
        expect(res.name).toBe("mike");
      });
    });
    
    • 콜백함수의 결과를 임의로 지정
    • 지정한 값에 대해서 resolve된 결과를 미리 지정
    • 그 값을 예측값과 비교

    DB 값 삽입 없이 createUser테스트

    jest.mock("./fn");
    fn.createUser.mockReturnValue({ name: "mike" });
    
    test("유저를 만든다", () => {
      const user = fn.createUser("mike");
      expect(user.name).toBe("mike");
    });
    • 해당 함수에서 resolve가 잘되었다는 것을 전제로 결과 값을 지정
    • 해당 함수가 실행이 되는 것이 아니기 때문에 db에 실제로 정보가 삽입되지 않음

    기타 mocking method

    const mockFn = jest.fn();
    
    mockFn(10, 20);
    mockFn();
    mockFn(30, 40);
    
    test("한번 이상 호출?", () => {
      expect(mockFn).toBeCalled();
    });
    
    test("정확히 3번 호출?", () => {
      expect(mockFn).toBeCalledTimes(3);
    });
    
    test("10이랑 20을 인수로 전달받은 함수가 있는가", () => {
      expect(mockFn).toBeCalledWith(10, 20);
    });
    
    test("마지막 함수는 30이랑 40 받았음?", () => {
      expect(mockFn).lastCalledWith(30, 40);
    });
    
    • toBeCalled : 한 번이라도 호출
    • toBeCalledTimes : 몇 번 mock이 호출되었나
    • toBeCalledWith : 특정 인수를 전달받은 함수가 있는지
    • lastCalledWith : 마지막 호출받을 때 특정 인수 받았는지
Designed by Tistory.