들어가며
벨로퍼트님의 테스팅 튜토리얼을 공부한 내용을 정리하는 글입니다.
TDD ?
TDD는 테스트를 통해서 개발을 이끌어나가는 형태의 개발론이다.
먼저 테스트 코드를 작성하고, 구현을 하는 방식이라고 보면 된다.
TDD에는 3가지 절차가 있다.
실패
첫 번째 절차로 실패이다. 말 그대로 실패하는 테스트 케이스를 먼저 만드는 것이다.
상황에 따라 다르지만, 먼저 구현할 기능부터 하나씩 테스트 케이스를 작성한다.
성공
두 번째 절차로 성공이다.
앞서 작성한 실패하는 테스트 케이스를 통과시키기 위해서 코드를 작성하는 것을 말한다.
리팩토링
구현한 코드가 테스트를 통과한다면, 중복되는 코드 또는 개선시킬 방법이 있는 코드를 리팩토링 시킨다.
리팩토링을 했을 때 테스트 케이스가 성공하면 다른 기능을 다시 첫 번째 절차부터 수행한다.
즉,
실패 => 성공 => 리팩토링 => 실패 => 성공 ... 방식으로 개발하는 것이다.
TDD의 장점
TDD를 진행하면서 테스트 케이스를 작성할 때 작은 단위로 개발하기 때문에
테스트 코드가 방대해지지 않고, 코드의 모듈화가 잘 이루어진다.
또, TDD를 통해서 자연스럽게 테스트 커버리지가 높아진다.
기능을 구현할 때 테스트를 먼저 작성하기 때문에 당연한 결과라고 볼 수 있다.
테스트 커버리지가 높아진다는 것은 리팩토링이 쉬워지고 유지보수도 쉬워진단는 뜻이다.
TDD 연습해보기
배열이 주어졌을 때 최댓값, 최솟값, 평균, 중앙값, 최빈값을 구하는 함수를 구현해보자!
벨로퍼트님의 게시글에는 바로 정답이 나오기 때문에 이부분은 해당 코드를 보지 않고
내가따로 작성해보겠다!
최댓값
import { max } from "./stats";
describe("stats", () => {
test("Max Value : [1, 2, 3] = 3", () => {
const array = [1, 2, 3];
expect(max(array)).toBe(3);
});
});
다음과 같이 작성했다.
당연히 아직 stats에 아무것도 작성하지 않았기 때문에 max가 없다는 오류가 뜨고, 이제 max를 구현하자!
export const max = (array) => {
const sorting = [...array].sort((a, b) => b - a);
return sorting[0];
};
sort를 사용해서 내림차순으로 정렬한 배열에서 첫 번째 값을 return한다.
아~ 주 문제없이 동작하는데, 더 쉬운 방법이 있었다...
export const max = (array) => {
return Math.max(...array);
};
최솟값
import { max, min } from "./stats";
describe("stats", () => {
const array = [1, 2, 3];
test("Max Value : [1, 2, 3] = 3", () => {
expect(max(array)).toBe(3);
});
test("Min Value : [1, 2, 3] = 1", () => {
expect(min(array)).toBe(1);
});
});
최댓값과 크게 다르지 않다.
export const min = (array) => {
return Math.min(...array);
};
그리고 난 애송이가 아니다.
max가 있다면 당연히 min이 있을 것이기 때문에 min을 사용해서 작성했다.
평균값
test("Average : [1, 2, 3] = 2", () => {
expect(avg(array)).toBe(2);
});
평균을 구하는 테스트 케이스를 작성했다.
export const avg = (array) => {
const sum = array.reduce((prev, curr) => prev + curr, 0);
return sum / array.length;
};
reduce를 활용해서 풀었다.
그런데 좀 더 깔끔하게 만드는 방법이 있었다.
export const avg = (array) => {
return array.reduce((prev, curr, _, { length }) => prev + curr / length, 0);
};
(1 + 2 + 3 ) / 3이 1 / 3 + 2 / 3 + 3 / 3과 똑같다는 사실을 생각안하고 있었다.
이런 방식으로 해결하면 보다 깔끔하게 만들 수 있다.
중앙값
describe("stats", () => {
// ...
describe("Median", () => {
const array1 = [1, 2, 3];
const array2 = [1, 2, 3, 4];
test("case 1 : [1, 2, 3] = 2", () => {
expect(median(array1)).toBe(2);
});
test("case 2 : [1, 2, 3, 4] = 2.5", () => {
expect(median(array2)).toBe(2.5);
});
});
});
중앙값은 배열의 개수에 따라 구하는 방법이 달라진다.
배열이 홀수개라면 단순하게 가운데 값인 2가 중앙값이지만,
짝수개라면 2 + 3 / 2 인 2.5가 중앙값이 된다.
그래서 describe 안에 다시 describe으로 케이스를 나눠주었다.
하지만 test 안에서 describe을 사용할 순 없다.
export const median = (array) => {
const sorting = [...array].sort((a, b) => a - b);
const center = Math.floor(sorting.length / 2);
return center % 2
? sorting[center]
: (sorting[center - 1] + sorting[center]) / 2;
};
최빈값
describe("Mode", () => {
const array1 = [1, 2, 2, 2, 3];
const array2 = [1, 2, 3];
const array3 = [1, 2, 2, 3, 3];
test("case 1 : [1, 2, 2, 2, 3] = 2", () => {
expect(mode(array1)).toBe(2);
});
test("case 2 : [1, 2, 3]", () => {
expect(mode(array2)).toBe(null);
});
test("case 3 : [1, 2, 2, 3, 3] = [2, 3]", () => {
expect(mode(array3)).toEqual([2, 3]);
});
});
최빈값은 배열에서 빈도가 가장 높은 값을 나타낸다. 이때 상황에 따라 결과가 3가지로 나눠진다.
1. 주어진 값 중 가장 자주 나타난 값이 하나인 경우 가장 높은 값이 최빈값이다.
[1, 2, 2, 2, 3] => 2
2. 모든 값이 빈도가 같은 경우 최빈값은 없다.
[1, 2, 3] => null
3. 빈도가 같은 값이 여러개인 경우, 모두 최빈값이다.
[1, 2, 2, 3, 3] => [2, 3]
export const mode = (array) => {
const frequency = new Map();
const set = new Set(array);
array.forEach((value) => {
frequency.set(value, frequency.get(value) ? frequency.get(value) + 1 : 1);
});
const maxValue = Math.max(...frequency.values());
const result = [...frequency.keys()].filter(
(number) => frequency.get(number) === maxValue
);
if (result.length === set.size) return null;
else if (result.length === 1) return result[0];
else return result;
};
map과 set을 사용해서 문제를 해결했다.
여기서 forEach를 대신해서 reduce를 사용하면 더 깔끔하게 해결이 가능하다.
export const mode = (array) => {
const set = new Set(array);
const frequency = array.reduce(
(acc, cur) => acc.set(cur, acc.get(cur) ? acc.get(cur) + 1 : 1),
new Map()
);
const maxValue = Math.max(...frequency.values());
const result = [...frequency.keys()].filter(
(number) => frequency.get(number) === maxValue
);
if (result.length === set.size) return null;
else if (result.length === 1) return result[0];
else return result;
};
정리
TDD를 사용했을 때 확실히 테스트 코드가 존재하기 때문에 리팩토링이 편하다.
하지만 아직 이것을 프론트엔드에서 어떤 방식으로 사용하면 좋을지는 감이 잡히지 않는다.
추후, 공부할 내용이 기대된다!
'React > 실험실' 카테고리의 다른 글
[React] 벨로퍼트와 함께하는 React Testing - Todo List (0) | 2023.01.07 |
---|---|
[React] 벨로퍼트와 함께하는 React Testing - react-testing-library (0) | 2023.01.06 |
[React] 성능 개선기 (0) | 2022.12.21 |
[React] form 태그를 사용해서 value 가져오기 feat. TypeScript (0) | 2022.12.18 |
[React] React-Query - useQuery (0) | 2022.12.13 |