글의 목적
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 );
expect (values).toBeGreaterThanOrEqual(4 );
expect (values).toBeLessThan(5 );
expect (values).toBeLessThanOrEqual(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를 확실하게 더 잘 다루고 싶다고 생각했다.