💡 jest ?
- 자바스크립트 테스트용 프레임워크로, 검증을 실행하는 matcher와 모킹에 필요한 api를 제공해주어 많은 사람들이 사용하고 있다.
- Node.js 환경에서 테스트를 실행하기 때문에 브라우저 환경에서의 DOM 조작 같은 검증은 불가능하다는 특징을 가지고 있지만,
- jest가 제공하는 jsdom 환경을 사용하면 브라우저와 동일하게 DOM 에 대한 테스트 검증이 가능해진다.
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 ?
- 값이 특정 조건 만족하는지 검증할 수 있는 집합을 말한다.
- 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를 사용해 특정 함수가 정상적으로 호출되었는지 검증한다.
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);
});
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();
});