
강의 목표
강의시간에 학습한 내용을 정리합니다.
강의 내용 정리
강의시간에 학습한 내용을 정리합니다.
실습 - Matcher를 이용해 유닛 테스트 작성하기 1
- 기존 next.js 프로젝트 계속 사용
- 다음 링크 참조하여 jest 설치 및 실행 환경 설정
- 테스트할 함수는 아래 코드를 참조
- 루트 위치에 /utils/chunk.ts 파일 생성 후 위 링크의 코드 복사
- chunk.ts 파일과 같은 경로에 chunk.test.ts 파일 생성 후 테스트 코드 작성
- 테스트를 실행시키기 위해 프로덕션 코드에 약간의 변경 필요
- chunk 함수의 파라미터와 관련하여 코드가 수정될 부분 존재
-> 이 약점(버그) 찾아서 테스트 코드 작성하고 프로덕션 코드도 수정
// 테스트할 코드
/**
* 'size' 길이만큼 그룹으로 분할된 요소 배열을 만듭니다. `배열`을 균등하게 분할할 수 없는 경우
* 최종 청크는 나머지 요소가 됩니다.
*
* @param {Array} array 배열을 처리할 대상
* @param {number} [size=1] 분할할 크기
* @returns {Array} 새로운 배열을 반환
* @example
*
* chunk(['a', 'b', 'c', 'd'], 2)
* // => [['a', 'b'], ['c', 'd']]
*
* chunk(['a', 'b', 'c', 'd'], 3)
* // => [['a', 'b', 'c'], ['d']]
*/
function chunk(array: any[], size: number = 1) {
size = Math.max(size, 0);
const length = array == null ? 0 : array.length;
if (!length || size < 1) {
return [];
}
let index = 0;
let resIndex = 0;
const result = new Array(Math.ceil(length / size));
while (index < length) {
result[resIndex++] = array.slice(index, (index += size));
}
return result;
}
// 정답코드
import { chunk } from "./chunk";
describe("chunk", () => {
const array = [0, 1, 2, 3, 4, 5];
it("should return chunked arrays", () => {
const actual = chunk(array, 3);
expect(actual).toEqual([
[0, 1, 2],
[3, 4, 5],
]);
});
it("should return the last chunk as remaining elements", () => {
const actual = chunk(array, 4);
expect(actual).toEqual([
[0, 1, 2, 3],
[4, 5],
]);
});
it("should coerce `size` to an integer", () => {
expect(chunk(array, array.length / 4)).toEqual([
[0],
[1],
[2],
[3],
[4],
[5],
]);
});
});
실습- Matcher를 이용해 유닛 테스트 작성하기 2
// 테스트할 코드
const htmlEscapes: Record<string, string> = {
"&": "&",
"<": "<",
">": ">",
'"': """,
"'": "'",
};
const htmlUnescapes: Record<string, string> = {
"&": "&",
"<": "<",
">": ">",
""": '"',
"'": "'",
};
const reUnescapedHtml = /[&<>"']/g;
const reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
const reEscapedHtml = /&(?:amp|lt|gt|quot|#(0+)?39);/g;
const reHasEscapedHtml = RegExp(reEscapedHtml.source);
/**
* 문자 "&", "<", ">", '"' 및 "'"를 해당 HTML 엔터티로 변환합니다.
*
* @param {string} [string=''] 변환할 문자열.
* @returns {string} 변환된 문자열을 반환합니다.
* @example
*
* escape('fred, barney, & pebbles')
* // => 'fred, barney, & pebbles'
*/
function escape(string: string) {
return string && reHasUnescapedHtml.test(string)
? string.replace(reUnescapedHtml, (chr) => htmlEscapes[chr])
: string || "";
}
/**
* 이 메서드는 `escape`의 역으로 `string`의 HTML 엔터티 `&`, `<`, `>`, `"` 및
* `'`를 해당 문자로 변환합니다.
*
* @param {string} [string=''] 변환할 문자열.
* @returns {string} 변환된 문자열을 반환합니다.
* @example
*
* unescape('fred, barney, & pebbles')
* // => 'fred, barney, & pebbles'
*/
function unescape(string: string) {
return string && reHasEscapedHtml.test(string)
? string.replace(reEscapedHtml, (entity) => htmlUnescapes[entity] || "'")
: string || "";
}
export { escape, unescape };
// 정답 코드
import { escape, unescape } from "./htmlEscape";
describe("escape", () => {
let escaped = "&<>"'/";
let unescaped = "&<>\"'/";
escaped += escaped;
unescaped += unescaped;
it("should escape values", () => {
expect(escape(unescaped)).toBe(escaped);
});
it("should handle strings with nothing to escape", () => {
expect(escape("abc")).toBe("abc");
});
it("should escape the same characters unescaped by `_.unescape`", () => {
expect(escape(unescape(escaped))).toBe(escaped);
});
["`", "/"].forEach((chr) => {
it(`should not escape the "${chr}" character`, () => {
expect(escape(chr)).toBe(chr);
});
});
});
describe("unescape", () => {
let escaped = "&<>"'/";
let unescaped = "&<>\"'/";
escaped += escaped;
unescaped += unescaped;
it("should unescape entities in order", () => {
expect(unescape("&lt;")).toBe("<");
});
it("should unescape the proper entities", () => {
expect(unescape(escaped)).toBe(unescaped);
});
it("should handle strings with nothing to unescape", () => {
expect(unescape("abc")).toBe("abc");
});
it("should unescape the same characters escaped by `_.escape`", () => {
expect(unescape(escape(unescaped))).toBe(unescaped);
});
it("should handle leading zeros in html entities", () => {
expect(unescape("'")).toBe("'");
expect(unescape("'")).toBe("'");
expect(unescape("'")).toBe("'");
});
["`", "/"].forEach((entity) => {
it(`should not unescape the "${entity}" entity`, () => {
expect(unescape(entity)).toBe(entity);
});
});
});
실습- .not 수식어를 이용해 유닛 테스트 작성하기
- 기존 next.js 프로젝트 계속 사용
- 함수의 명세를 잘 읽고 필요한 테스트들 작성
-> .not 수식어를 사용하는 테스트도 작성할 것
// 테스트할 코드
/**
* 섞인 값의 배열을 만듭니다.
*
* @param {Array} array 작업을 처리할 배열
* @returns {Array} 새로운 배열을 반환
* @example
*
* shuffle([1, 2, 3, 4])
* // => [4, 1, 3, 2]
*/
function shuffle(array: any[]) {
const length = array == null ? 0 : array.length;
if (!length) {
return [];
}
let index = -1;
const lastIndex = length - 1;
const result = [...array];
while (++index < length) {
const rand = index + Math.floor(Math.random() * (lastIndex - index + 1));
const value = result[rand];
result[rand] = result[index];
result[index] = value;
}
return result;
}
export default shuffle;
// 정답 코드
import shuffle from "./shuffle";
describe("shuffle", () => {
const array = [1, 2, 3];
it("should return a new array", () => {
expect(shuffle(array)).not.toBe(array);
});
it("should contain the same elements after a collection is shuffled", () => {
expect(shuffle(array).sort()).toEqual(array);
});
});
실습 - 비동기 코드 유닛 테스트 작성하기
- 기존 next.js 프로젝트 계속 사용
- 함수의 명세를 잘 읽고 필요한 테스트들 작성
- done 인자를 이용해 작성할 것
- mock 함수(jest.fn())와 toHaveBeenCalledWith matcher를 이용한 함수도 작성할
// 테스트할 코드
/**
* 'wait' 밀리초 후에 'func'를 호출합니다.
*
* @param {Function} func 호출할 함수.
* @param {number} wait 지연할 밀리초.
* @param {...*} [args] 함수에 전달할 인수.
* @returns {number} 타이머 ID.
* @example
*
* delay(text => console.log(text), 1000, 'later')
* // => Logs 'later' after one second.
*/
function delay(func: Function, wait = 0, ...args: any[]) {
if (typeof func !== "function") {
throw new TypeError("Expected a function");
}
return setTimeout(func, +wait, ...args);
}
export default delay;
// 정답 코드
import delay from "./delay";
describe("delay", () => {
it("should delay `func` execution", (done) => {
let pass = false;
delay(() => {
pass = true;
}, 32);
setTimeout(() => {
expect(pass).toBe(false);
}, 1);
setTimeout(() => {
expect(pass);
done();
}, 64);
});
it("should use a default `wait` of `0`", (done) => {
let pass = false;
delay(() => {
pass = true;
});
expect(pass).toBe(false);
setTimeout(() => {
expect(pass);
done();
}, 0);
});
it("should be cancelable", (done) => {
let pass = true;
const timerId = delay(() => {
pass = false;
}, 32);
clearTimeout(timerId);
setTimeout(() => {
expect(pass);
done();
}, 64);
});
it("should provide additional arguments to `func`", (done) => {
const mockFn = jest.fn();
delay(mockFn, 32, 1, 2);
setTimeout(() => {
expect(mockFn).toHaveBeenCalledWith(1, 2);
done();
}, 64);
});
});반응형
'교육 > 코드잇 스프린트 : 단기심화 5기' 카테고리의 다른 글
| [ 코드잇 스프린트 ] 교육기간 9일차 TIL (1) | 2024.11.21 |
|---|---|
| [ 코드잇 스프린트 ] 교육기간 8일차 TIL (1) | 2024.11.18 |
| [ 코드잇 스프린트 ] 교육기간 6일차 TIL (3) | 2024.11.15 |
| [ 코드잇 스프린트 ] 교육기간 5일차 TIL (1) | 2024.11.14 |
| [ 코드잇 스프린트 ] 교육기간 4일차 TIL (3) | 2024.11.13 |
