글의 목적
jest 메서드를 정리하고 익숙해질때까지 참고하려는 목적.
그 동안 테스트코드를 작성하기 보다는 postman으로 요청을 달리하면서 확인만 했었기 때문에 테스트코드 작성을 습관하하려는 하나의 속셈.
리팩토링2탄의 이해도를 높이기 위한 하나의 방편. *나의 글이 jest를 사용하여 테스트코드를 작성하고 싶은 모든 개발자에게 작은 도움이 될 수 있기를...
본문
0. jest의 간단한 사용
function sum(a, b) {
return a + b;
}
module.exports = sum;
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를 확실하게 더 잘 다루고 싶다고 생각했다.