본문 바로가기

데일리

[23.10.15] jest 메서드 정리

글의 목적

  1. jest 메서드를 정리하고 익숙해질때까지 참고하려는 목적.
  2. 그 동안 테스트코드를 작성하기 보다는 postman으로 요청을 달리하면서 확인만 했었기 때문에 테스트코드 작성을 습관하하려는 하나의 속셈.
  3. 리팩토링2탄의 이해도를 높이기 위한 하나의 방편.
    *나의 글이 jest를 사용하여 테스트코드를 작성하고 싶은 모든 개발자에게 작은 도움이 될 수 있기를...

본문

0. jest의 간단한 사용

  • sum.js
function sum(a, b) {
  return a + b;
}
module.exports = sum;
  • sum.test.js
const sum = require('./sum'); 

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3); // 일치
});

1. jest 메서드 정리

expect().not.toBe();

  • 기대하는것과 일치하지 않는값
test('2 + 2 는 4', () => {
  expect(2 + 2).not.toBe(5); // 일치
});

expect().toEqual();

  • 기대하는것과 기대하는 오브젝트 or 배열
  • 엄격하지 않음.
test('Obj 할당', () => {
  const data = {one: 1};
  data['two'] = 2;
  expect(data).toEqual({one: 1, two: 2}); // 일치
});

expect().toStrictEqual();

  • 기대하는것과 기대하는 오브젝트 or 배열
  • 엄격함.
test('Obj 할당', () => {
  const data = {one: 1};
  data['two'] = 2;
  expect(data).toEqual({one: 1, two: 2}); // 일치
});

toEqual() vs toStrictEqual() 비교

  • 예시코드를 보면 바로 이해할 수 있을 것임.
const userA = {
  name: 'Alice',
  age: 30,
  address: {
    city: 'New York',
    zipCode: '10001',
  },
  hobbies: ['reading', 'traveling'],
};

const userB = {
  name: 'Alice',
  age: 30,
  address: {
    city: 'New York',
    zipCode: '10001',
  },
  hobbies: ['reading', 'cooking'],
};

expect(userA).toEqual(userB); // 일치
expect(userA).toStrictEqual(userB); // 불일치

expect(n).toBeNull();

  • 기대하는것과 기대하는값이 null
  • .not은 반대임

expect(n).toBeUndefined();

  • 기대하는것과 기대하는값이 undefined
  • .not은 반대임

expect(n).toBeDefined();

  • 기대하는것과 기대하는값이 undefined가 아닌것.
  • .not은 반대임

expect(n).toBeTruthy();

  • 기대하는것과 기대하는값이 true
  • .not은 반대임

expect(n).toBeFalsy();

  • 기대하는것과 기대하는값이 false
  • .not은 반대임

toBeNull() vs toBeUndefined() vs toBeDefined() vs toBeTruthy() vs toBeFalsy() 비교

  • 예시코드를 보면 바로 이해할 수 있을 것임.
test('null', () => {
  const n = null;
  expect(n).toBeNull(); // 일치
  expect(n).toBeDefined(); // 일치
  expect(n).not.toBeUndefined(); // 일치
  expect(n).not.toBeTruthy(); // 일치
  expect(n).toBeFalsy(); // 일치
});

test('zero', () => {
  const z = 0;
  expect(z).not.toBeNull(); // 일치
  expect(z).toBeDefined(); // 일치
  expect(z).not.toBeUndefined(); // 일치
  expect(z).not.toBeTruthy(); // 일치
  expect(z).toBeFalsy(); // 일치
});

expect().toBeGreaterThan();

  • 기대하는것이 기대하는값보다 크다.(>)

expect().toBeGreaterThanOrEqual();

  • 기대하는것이 기대하는값보다 크거나 같다.(>=)

expect().toBeLessThan();

  • 기대하는것이 기대하는값보다 작다.(<)

expect().toBeLessThanOrEqual();

  • 기대하는것이 기대하는값보다 작거나 같다.(<=)

toBeGreaterThan() vs toBeGreaterThanOrEqual() vs toBeLessThan() vs toBeLessThanOrEqual()

  • 예시코드를 보면 바로 이해할 수 있을 것임.
test("수 비교", () => {
  const values = 4;
  expect(values).toBeGreaterThan(3); // 4 > 3 일치
  expect(values).toBeGreaterThanOrEqual(4); // 4 >= 3 일치
  expect(values).toBeLessThan(5); // 4 < 5 일치
  expect(values).toBeLessThanOrEqual(4); // 4 <= 4 일치
});

expect().toBeCloseTo();

  • 소수값을 정확하게 비교하고 싶을때는 toBeCloseTo()를 사용해야함.
  • 특정 소수를 이진법을 바꾸는 과정에서 무한소수가 나와서 값이 다르게 찍힘.
test("소수 비교", () => {
  const f = { add: (a, b) => a + b };
  expect(f.add(0.1, 0.2)).toBe(0.3); // 불일치
  expect(f.add(0.1, 0.2)).toBeCloseTo(0.3); // 일치
});

expect().toMatch();

  • 기대하는문자열에 기대하는문자가 있는지
test("알파벳이 있는지(대소문자 구분o)", () => {
  expect("team").not.toMatch(/T/); // 일치
});

test("알파벳이 있는지(대소문자 구분x)", () => {
  expect("team").toMatch(/T/i); // 일치
});

expect().toContain();

  • 기대하는배열에 기대하는값이 있는지
test("배열에 값이 있는지", () => {
  const userList = ["test1", "test2", "test3"];
  const userIn = "test3";
  const userOut = "test4";
  expect(userList).toContain(userIn); // 일치
  expect(userList).not.toContain(userOut); // 일치
});

expect().toThrow();

  • 기대하는 함수에 기대하는에러와 동일한 에러메시지 발생하는지
test("에러가 발생하는지", () => {
  function error() {
    throw new Error("error");
  }
  expect(() => error()).toThrow(); // 일치
  expect(() => error()).toThrow("error"); // 일치
  expect(() => error()).toThrow("error message"); // 불일치
});

2. jest 비동기 처리

아직 준비중.

  • (사이드프로젝트의 service코드를 jest로 작성하고 요약하고 싶어서 미루는중.)

3. jest 전후처리

전후처리 실행순서

  • 개인적인 생각으로 실행순서만 명확하게 알고 있으면 될 것 같다.
  • 좀 헷갈리겠지만 메서드들의 이름을 생각하면 감이 잡힌다.
  • 1
  • 2, 3, 4
  • 5, 6, 7
  • 8
  • 9, 10, 11, 12, 13
  • 14, 15, 16, 17, 18
  • 19
  • 20
  • 너무 헷갈리면 이렇게 묶어서 보고 감을 찾으시기를!!
beforeAll(() => console.log("1", "밖 beforeAll"));
beforeEach(() => console.log("2, 5, 9, 14", "밖 beforeEach"));
afterEach(() => console.log("4, 7, 13, 18", "밖 afterEach"));
afterAll(() => console.log("20", "밖 afterAll"));

test("1 + 1 = 2", () => {
  console.log("3", "밖 test 1");
  expect(fn.add(1, 1)).toBe(2);
});

test("1 + 1 = 2", () => {
  console.log("6", "밖 test 2");
  expect(fn.add(1, 1)).toBe(2);
});

describe("내부 test", () => {
  beforeAll(() => console.log("8", "안 beforeAll"));
  beforeEach(() => console.log("10, 15", "안 beforeEach"));
  afterEach(() => console.log("12, 17", "안 afterEach"));
  afterAll(() => console.log("19", "안 afterAll"));

  test("1 + 1 = 2", () => {
    console.log("11", "안 test 1");
    expect(fn.add(1, 1)).toBe(2);
  });

  test("1 + 1 = 2", () => {
    console.log("16", "안 test 2");
    expect(fn.add(1, 1)).toBe(2);
  });
});

test.only

  • 테스트 코드를 작성하다가 코드가 맞는것 같은데 틀렸다고 나왔다면 바로 수정하기 보다는 하나의 테스트 코드만 먼저 실행시켜 보고 전후처리과정에서 이상이 있는지 없는지 먼저 파악하는게 좋다.
  • 그때 test뒤에 .only만 넣어주면 이 지정된 테스트만 실행된다.

test.skip

  • 마찬가지로 다른 테스트에 영향을 주는 테스트를 발견했지만 이 테스트를 바로 수정할 수 없다면
  • 그때 test뒤에 .skip만 넣어주면 이 지정된 테스트만 제외되고 실행된다.

4. jest mock

mock.calls

  • .mock 프로퍼티에는 calls라는 배열이 담겨 있고 배열속에는 호출되었던 인자가 담겨 있기 때문에 호출횟수와 호출인자를 파악할 수 있음.
const mockFn = jest.fn();

mockFn();
mockFn(1);
mockFn(999);

test("mockFn 은 3번 호출됨.", () => {
  console.log(mockFn.mock.calls);
  expect(mockFn.mock.calls.length).toBe(3);
});
test("mockFn 마지막에 호출된 인자는 999임.", () => {
  expect(mockFn.mock.calls[2][0]).toBe(999);
});

const mockFn = jest.fn();

function forEachAdd111(arr) {
  arr.forEach((num) => {
    mockFn(num + 111);
  });
}

forEachAdd111([222, 555, 888]);

test("mockFn 은 3번 호출됨.", () => {
  console.log(mockFn.mock.calls);
  expect(mockFn.mock.calls.length).toBe(3);
});
test("mockFn 마지막에 호출된 인자는 999임.", () => {
  expect(mockFn.mock.calls[2][0]).toBe(999);
});
test("mockFn 전달된 값은 333, 666, 999임.", () => {
  expect(mockFn.mock.calls[0][0]).toBe(333);
  expect(mockFn.mock.calls[1][0]).toBe(666);
  expect(mockFn.mock.calls[2][0]).toBe(999);
});

mock.results

  • mock 프로퍼티에는 results라는 객체도 담겨있다. 이것은 mockReturnValueOnce와 mockReturnValue로 활용할 수 있다.
const mockFn = jest.fn((num) => num + 111);

mockFn(222);
mockFn(555);
mockFn(888);

test("111이 증가한 333이 리턴됨.", () => {
  console.log(mockFn.mock.results);
  expect(mockFn.mock.results[0].value).toBe(333);
});
test("111이 증가한 666이 리턴됨.", () => {
  expect(mockFn.mock.results[1].value).toBe(666);
});
test("111이 증가한 999가 리턴됨.", () => {
  expect(mockFn.mock.results[2].value).toBe(999);
});

const mockFn = jest.fn();

mockFn.mockReturnValueOnce(333).mockReturnValueOnce(666).mockReturnValue(999);

mockFn();
mockFn();
mockFn();

test("리턴하고 싶은값이 리턴됨.(333)", () => {
  console.log(mockFn.mock.results);
  expect(mockFn.mock.results[0].value).toBe(333);
});
test("리턴하고 싶은값이 리턴됨.(666)", () => {
  expect(mockFn.mock.results[1].value).toBe(666);
});
test("리턴하고 싶은값이 리턴됨.(999)", () => {
  expect(mockFn.mock.results[2].value).toBe(999);
});

  • 이렇게 활용할 수도 있다.
const mockFn = jest.fn();

mockFn
  .mockReturnValueOnce(true)
  .mockReturnValueOnce(false)
  .mockReturnValueOnce(true)
  .mockReturnValueOnce(false)
  .mockReturnValue(true);

const result = [1, 2, 3, 4, 5].filter((num) => mockFn(num));

test("홀수는 1, 3, 5", () => {
  expect(result).toStrictEqual([1, 3, 5]);
});
  • mockResolvedValue를 활용하면 비동기도 받을 수 있다.
const mockFn = jest.fn();

mockFn.mockResolvedValue({ name: "test" });

const result = [1, 2, 3, 4, 5].filter((num) => mockFn(num));

test("user의 이름은 test", () => {
  mockFn().then((res) => {
    expect(res.name).toBe("test");
  });
});

활용가능한 matchers

const mockFn = jest.fn();

mockFn(10, 20);
mockFn(30, 40);
mockFn(50, 60);

test("한번이라도 호출된적 있음", () => {
  expect(mockFn).toBeCalled();
});
test("정확하게 3번 호출됨", () => {
  expect(mockFn).toBeCalledTimes(3);
});
test("10, 20을 전달받아서 호출된적 있음", () => {
  expect(mockFn).toBeCalledWith(10, 20);
});
test("마지막으로 50, 60을 전달받아 호출됨.", () => {
  expect(mockFn).lastCalledWith(50, 60);
});

결론

  • 실제 프로젝트에서는 다양하게 활용되는 테스트도구이기도 하고 지금은 유닛테스트에 사용되는것들 위주로 정리했지만 통합테스트에서는 더 다양하게 jest를 활용하기 때문에 더 공부해야될 것 같다.
  • orm도구를 사용해서 testdb에 직접연결하여 통합테스트를 진행하는 방법도 있지만 현재 orm없이 SQL에 익숙해지기 위해서 생쿼리로 프로젝트를 만들어 보고 있고 다양한 디자인패턴과 아키텍처, js에서 객체지향과 디펜던시를 어떻게 효율적으로 관리할 수 있을지에 대해서 test를 해보고 있기 때문에(nest.js랑 prisma를 쓰면되잖아!!! => 정답! ) jest를 확실하게 더 잘 다루고 싶다고 생각했다.

'데일리' 카테고리의 다른 글

[23.10.20] 프로토타입  (27) 2023.10.19
[23.10.19] 생성자 함수에 의한 객체 생성  (29) 2023.10.19
[23.10.18] 원시 값과 객체  (24) 2023.10.19
[23.10.17] 객체 리터럴  (50) 2023.10.19
[23.10.16] 테스트코드와 tdd에 대한 느낌  (52) 2023.10.19