SW
Jest 본문
0. Jest란?
- 페이스북에서 만든 테스트 프레임워크
1. 사용 방법
1-1. 설치
npm install jest --save-dev
1-2. package.json의 scripts를 다음과 같이 수정
1-3. 코드 작성
* fn.js
const fn = {
add: (num1, num2) => (num1 + num2),
};
module.exports = fn;
* fn.test.js
const fn = require('./fn');
test('1은 1이야.', () => {
expect(1).toBe(1);
});
test('2더하기 3은 5야.', () => {
expect(fn.add(2, 3)).toBe(5);
});
test('3더하기 3은 5야.', () => {
expect(fn.add(3, 3)).toBe(5);
});
test('3더하기 3은 5가 아니야.', () => {
expect(fn.add(3, 3)).not.toBe(5);
});
1-4. 실행
npm test
1-5. 결과값
2. Matcher
- toBe부분에서 사용하는 함수를 Matcher라고 한다.
- toBe는 숫자나 문자 등 기본 타입값을 비교할 때 사용하며 toBe 이외에도 toEqual 등 여러 Matcher가 존재한다.
- 자주 사용되는 Matcher
더보기toBe, toEqual, toStrictEqual, toBeNull, toBeUndefined, toBeDefined, toBeTruthy, toBeFalsy, toBeGreaterThan, toBeGreaterThanOrEqual, toBeLessThan, toBeLessThanOrEqual, toBeCloseTo, toMatch, toThrow - https://jestjs.io/docs/en/using-matchers
3. 비동기 코드 테스트
3-1. 코드 작성
const fn = {
getName: callback => {
const name = "Mike";
setTimeout(() => {
callback(name);
}, 3000);
},
};
module.exports = fn;
3-2. 테스트 코드
- 다음과 같이 코드를 작성하면 toBe에 Mike가 아닌 다른 값이 와도 테스트를 성공함
- Jest는 실행이 끝이 도달하게 되면 기다리지 않고 그대로 끝나게 된다.
const fn = require('./fn');
test('3초 후에 받아온 이름은 Mike', () => {
const callback = name => {
expect(name).toBe('Mike');
}
fn.getName(callback);
})
- 그러한 문제를 방지하기 위해 done이라고 하는 콜백함수를 전달해주면 된다.
- done이 호출될 때까지 jest는 테스트를 끝내지 않고 기다리게 된다.
const fn = require('./fn');
test('3초 후에 받아온 이름은 Mike', (done) => {
const callback = name => {
expect(name).toBe('Mike');
done();
}
fn.getName(callback);
})
3-3. api 에러와 같은 경우 에러를 감지하고 싶다면 try/catch 사용
- 코드
const fn = {
getName: callback => {
const name = "Mike";
setTimeout(() => {
// callback(name);
throw new Error("서버 에러..");
}, 3000);
},
};
module.exports = fn;
- 테스트 코드
const fn = require("./fn");
test("3초 후에 받아온 이름은 Mike", (done) => {
const callback = (name) => {
try {
expect(name).toBe("Mike");
done();
} catch (error) {
done();
}
};
fn.getName(callback);
});
- 결과
3-4. Promise 테스트 코드 : return을 붙여줘야 함
- 코드
const fn = {
getAge: () => {
const age = 30;
return new Promise((res, rej) => {
setTimeout(() => {
res(age);
}, 3000);
});
},
};
module.exports = fn;
- 테스트 코드
const fn = require("./fn");
test("3초 후에 받아온 나이는 30", () => {
return fn.getAge().then((age) => {
expect(age).toBe(30);
});
});
// resolves라는 Matcher를 사용한 경우
test("3초 후에 받아온 나이는 30", () => {
return expect(fn.getAge()).resolves.toBe(30);
});
// async/await 사용한 경우
test("3초 후에 받아온 나이는 30", async () => {
const age = await fn.getAge();
expect(age).toBe(30);
});
// resolves라는 Matcher와 async/await 사용한 경우
test("3초 후에 받아온 나이는 30", async () => {
await expect(fn.getAge()).resolves.toBe(30);
});
- 결과
4. 테스트 전후 작업
4-1. 코드 작성
- 코드
const fn = {
add: (num1, num2) => num1 + num2,
};
module.exports = fn;
- 테스트 코드
const fn = require("./fn");
let 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);
});
test("0 더하기 4은 4이야.", () => {
num = fn.add(num, 4);
expect(num).toBe(4);
});
- 결과
4-2. 무엇이 잘못되었나?
- num이 0으로 초기화되지 않고 누적되어 더해짐 => 각 함수 직전에 실행되는 beforeEach라는 Helper 함수를 사용
4-3. beforeEach, afterEach
- beforeEach 테스트 코드
const fn = require("./fn");
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);
});
test("0 더하기 4은 4이야.", () => {
num = fn.add(num, 4);
expect(num).toBe(4);
});
- beforeEach 결과
- afterEach 코드(초기값을 10으로 설정)
const fn = require("./fn");
let num = 0;
afterEach(() => {
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);
});
test("0 더하기 4은 4이야.", () => {
num = fn.add(num, 4);
expect(num).toBe(4);
});
- afterEach 결과
4-4. 만약 전후작업이 시간이 오래걸리는 작업이라면?
- DB를 연결하고 끊는 작업을 매번 테스트마다 한다고 가정해보자.
- 코드
const fn = {
connectUserDb: () => {
return new Promise((res) => {
setTimeout(() => {
res({
name: "Mike",
age: 30,
gender: "male",
});
}, 500);
});
},
disconnectDb: () => {
return new Promise((res) => {
setTimeout(() => {
res();
}, 500);
});
},
};
module.exports = fn;
- 테스트 코드
const fn = require("./fn");
let user;
beforeEach(async () => {
user = await fn.connectUserDb();
});
afterEach(() => {
return fn.disconnectDb();
});
test("이름은 Mike", () => {
expect(user.name).toBe("Mike");
});
test("나이는 30", () => {
expect(user.age).toBe(30);
});
test("성별은 남성", () => {
expect(user.gender).toBe("male");
});
- 결과 : 각 작업마다 시간이 1초 이상 소요된다. 그런데 이 예시처럼 DB를 연결하고 끊는 작업을 매번 해줘야할까? 아니다. 최초에 한번, 최후에 한번만 실행해주면 된다. 이때, 사용할 수 있는 것이 beforeAll, afterAll 이다.
- beforeAll, afterAll 사용한 테스트 코드
const fn = require("./fn");
let user;
beforeAll(async () => {
user = await fn.connectUserDb();
});
afterAll(() => {
return fn.disconnectDb();
});
test("이름은 Mike", () => {
expect(user.name).toBe("Mike");
});
test("나이는 30", () => {
expect(user.age).toBe(30);
});
test("성별은 남성", () => {
expect(user.gender).toBe("male");
});
- beforeAll, afterAll 사용 결과 : 시간이 단축되었다.
'대학교 > FE' 카테고리의 다른 글
[Apollo-Client] useQuery사용 시 update 방법 (0) | 2020.08.12 |
---|---|
[React & RN] Styled Components (0) | 2020.04.14 |
[React Hooks] Hooks (2) (0) | 2020.04.07 |
[React-Native] react-native init (0) | 2020.04.02 |
[Redux] Pure Redux: To Do List (0) | 2020.04.01 |