이전 글 : [Canvas-불꽃놀이-26] TextData 클래스 단위 테스트
캔버스의 여러 속성을 설정할 CanvasOption 클래스 단위 테스트
CanvasOption 클래스 단위 테스트
드디어 캔버스 테스트로 넘어왔다.
CanvasOption 클래스는 캔버스 렌더링 및 속성 설정(DPI, 크기, 폰트 등)을 관리한다.
또한 반응형을 고려한 canvas의 크기와 여러 좌표값들을 변경해야 의도한 모습이 나오므로 오류 없이 수행돼야 원하는 결과를 얻을 수 있다.
CanvasOption 클래스 전체 코드
더보기
import { ANIMATION, SCREEN, POS, FONT } from "@/js/constants.js";
class CanvasOption {
/**
* 캔버스 기본 설정을 관리하고 초기화하는 클래스
* - 캔버스 엘리먼트를 정의하고, 물리적 및 CSS 크기, DPI, 폰트 설정 등을 처리
* - 화면 크기에 따라 캔버스 옵션을 동적으로 조정
*/
constructor() {
this.canvas = document.getElementById("canvas");
if (!this.canvas) {
throw new Error("캔버스 객체를 발견하지 못했습니다. 다시 확인해주세요.");
}
this.ctx = this.canvas.getContext("2d", { willReadFrequently: true });
this.fps = ANIMATION.FPS;
this.interval = 1000 / this.fps;
this.initCanvasOptionVars();
}
init() {
this.initCanvasOptionVars();
}
initCanvasOptionVars() {
this.dpr = Math.min(Math.round(window.devicePixelRatio), SCREEN.MAX_DPR) || 1;
this.canvasCssWidth = window.innerWidth;
this.canvasCssHeight = window.innerHeight;
this.isSmallScreen = window.matchMedia(`(max-width: ${SCREEN.SMALL_WIDTH}px)`).matches;
this.mainX = this.canvasCssWidth / POS.MAIN_X_DIVISOR;
this.mainY = Math.floor(this.canvasCssHeight * POS.MAIN_Y_RATIO);
this.mainFontSize = this.setMainFontSize();
this.subFontSize = this.setSubFontSize();
}
/**
* @param {number} [fontSize] 폰트 사이즈, 없을 시 화면 크기에 따라 설정
* @returns {number} 메인 폰트 사이즈 반환 (작은 화면일 경우 별도의 비율 적용)
*/
setMainFontSize(fontSize) {
if (!fontSize) {
const ratio = this.isSmallScreen ? FONT.MAIN_RATIO_SMALL : FONT.MAIN_RATIO_GENERAL;
fontSize = ((this.canvasCssWidth + this.canvasCssHeight) / 2) * ratio;
}
return Math.round(fontSize);
}
/**
* @returns {number} 서브 폰트 사이즈 반환
*/
setSubFontSize() {
return Math.round(this.mainFontSize * FONT.SUB_RATIO);
}
/**
* @param {number} fontSize
* @returns ctx font 설정을 위한 문자열 반환
*/
setFontStyle(fontSize) {
return `${fontSize}px ${FONT.FAMILY}`;
}
/**
* @param {string} color
*/
fillFullCanvas(color) {
this.ctx.fillStyle = color;
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
}
/**
* @param {number} x
* @param {number} y
* @returns 파티클이 캔버스 영역을 벗어날 경우 true를 반환, 영역안이면 false를 반환
*/
isOutOfCanvasArea(x, y) {
return x < 0 || x > this.canvasCssWidth || y < 0 || y > this.canvasCssHeight;
}
}
export default CanvasOption;
1. 테스트 공통 설정하기
- 테스트마다 CanvasOption의 인스턴스를 생성해야하니 beforeEach로 이 과정을 처리한다.
describe("CanvasOption 테스트 (jest-canvas-mock 활용)", () => {
// CanvasOption 인스턴스
let canvasOption;
// 테스트 시작전 캔버스를 세팅하고 인스턴스를 생성한다.
beforeEach(() => {
setTestCanvas();
canvasOption = new CanvasOption();
});
// 생략...
}
2. 캔버스 생성자와 멤버변수 초기화 테스트
캔버스 객체를 잘 가져오는지, 가져오지 못한다면 지정한 오류를 처리하는지와 멤버변수가 의도한 대로 설정되는지 검증하도록 한다.
- canvas element와 context 설정 여부 테스트
// CanvasOption.test.js
test("캔버스 객체 관련 멤버변수 초기화", () => {
// 캔버스 엘리먼트와 context가 정상적으로 생성되는지 검증
expect(canvasOption.canvas).toBeInstanceOf(HTMLCanvasElement);
expect(canvasOption.ctx).toBeInstanceOf(CanvasRenderingContext2D);
});
- canvas element를 가져오지 못할 경우 에러를 발생시키는지 테스트
// CanvasOption.test.js
test("캔버스 객체를 불러오지 못할 경우 에러 처리", () => {
// 캔버스 객체를 삭제하여 오류를 던지는지 확인한다.
document.getElementById(TEST_OPTION.CANVAS_ID).remove();
expect(() => new CanvasOption()).toThrow("캔버스 객체를 발견하지 못했습니다. 다시 확인해주세요.");
});
- 애니메이션 관련 멤버 변수 검증
// CanvasOption.test.js
test("애니메이션 관련 멤버변수 초기화", () => {
expect(canvasOption.fps).toBe(ANIMATION.FPS);
expect(canvasOption.interval).toBe(1000 / canvasOption.fps);
expect(canvasOption.dpr).toBeGreaterThanOrEqual(1);
expect(canvasOption.dpr).toBeLessThanOrEqual(SCREEN.MAX_DPR);
});
- 캔버스 CSS 크기와, 텍스트 픽셀데이터를 뽑고, 불꽃놀이의 메인이 되는 x, y좌표 검증
// CanvasOption.test.js
test("캔버스 css 크기, 메인좌표 관련 멤버변수 초기화", () => {
expect(canvasOption.canvasCssWidth).toBe(INNER_WIDTH);
expect(canvasOption.canvasCssHeight).toBe(INNER_HEIGHT);
expect(canvasOption.mainX).toBe(INNER_WIDTH / POS.MAIN_X_DIVISOR);
expect(canvasOption.mainY).toBe(Math.floor(INNER_HEIGHT * POS.MAIN_Y_RATIO));
});
- PC화면과 모바일 화면(480px 미만) 여부 검증
// CanvasOption.test.js
test("PC 화면일때", () => {
expect(canvasOption.isSmallScreen).toBe(false);
});
test("모바일 화면일때", () => {
defineDomObjectProperty({ domObj: window, property: TYPE_INNER_WIDTH, value: SMALL_INNER_WIDTH });
canvasOption.initCanvasOptionVars();
expect(canvasOption.isSmallScreen).toBe(true);
});
- PC화면과 모바일화면일때 메인 폰트와 서브 폰트의 사이즈 검증
// CanvasOption.test.js
test.each([
{ fontsize: undefined, testWidth: INNER_WIDTH, notice: "PC 화면-인자 전달하지 않아 기본값 적용" },
{ fontsize: 100, testWidth: SMALL_INNER_WIDTH, notice: "모바일 화면-인자 100 전달" },
])("mainFontSize($fontsize) 테스트 ($notice)", ({ fontsize, testWidth }) => {
// 화면 사이즈 세팅
defineDomObjectProperty({ domObj: window, property: TYPE_INNER_WIDTH, value: testWidth });
canvasOption.initCanvasOptionVars();
// 화면별 폰트 사이즈별 달라지는 비율 적용
const result = canvasOption.setMainFontSize(fontsize);
const ratio = canvasOption.isSmallScreen ? FONT.MAIN_RATIO_SMALL : FONT.MAIN_RATIO_GENERAL;
const expectedResult = fontsize ?? Math.round(((testWidth + INNER_HEIGHT) / 2) * ratio);
// 예상값과 결과값이 일치하는지 확인
expect(result).toBe(expectedResult);
expect(canvasOption.isSmallScreen).toBe(testWidth <= SMALL_INNER_WIDTH);
});
test("subFontSize 테스트", () => {
const result = canvasOption.setSubFontSize();
expect(result).toBe(Math.round(canvasOption.mainFontSize * FONT.SUB_RATIO));
});
3. 캔버스 전체 화면 덮기 테스트
- 캔버스 전체 화면을 지정한 색상으로 채우는지 검증
// CanvasOption.test.js
test("fillFullCanvas 테스트", () => {
canvasOption.fillFullCanvas("#121212");
// fillStyle이 예상값과 같은지 검증
expect(canvasOption.ctx.fillStyle).toBe("#121212");
// 실제 fillRect를 호출할 때의 인자와 테스트 인자가 동일한지 검증
expect(canvasOption.ctx.fillRect).toHaveBeenCalledWith(0, 0, canvasOption.canvas.width, canvasOption.canvas.height);
});
4. 파티클의 좌표값이 캔버스 영역을 벗어났는지 테스트
- 파티클의 좌표값이 캔버스 영역 밖을 벗어난 경우 애니메이션 업데이트와 그리는 걸 진행할 필요가 없다.
이 메서드를 통해 영억을 벗어나면 파티클 배열에서 제외시키면 메모리 효율성도 좋아진다.
// CanvasOption.test.js
test.each([
{ x: 10, y: 10, expected: false, notice: "캔버스 내부" },
{ x: INNER_WIDTH, y: INNER_HEIGHT, expected: false, notice: "캔버스 내부" },
{ x: -10, y: 10, expected: true, notice: "캔버스 왼쪽 밖" },
{ x: 10, y: -10, expected: true, notice: "캔버스 위쪽 밖" },
{ x: -10, y: -10, expected: true, notice: "캔버스 왼쪽, 위쪽 밖" },
{ x: INNER_WIDTH + 10, y: 10, expected: true, notice: "캔버스 오른쪽 밖" },
{ x: 10, y: INNER_HEIGHT + 10, expected: true, notice: "캔버스 아래쪽 밖" },
{ x: INNER_WIDTH + 10, y: INNER_HEIGHT + 10, expected: true, notice: "캔버스 오른쪽, 아래쪽 밖" },
])("isOutOfCanvas($x, $y) 테스트 / $notice", ({ x, y, expected }) => {
expect(canvasOption.isOutOfCanvasArea(x, y)).toBe(expected);
});
테스트 결과
npx jest .__test__/canvas/CanvasOption.test.js
Github Repo
'Canvas > 불꽃놀이 프로젝트' 카테고리의 다른 글
[Canvas-불꽃놀이-29] Canvas 클래스 단위 테스트 (텍스트 데이터 생성) (1) | 2024.12.17 |
---|---|
[Canvas-불꽃놀이-28] Canvas 클래스 단위 테스트 (초기화) (1) | 2024.12.17 |
[Canvas-불꽃놀이-26] TextData 클래스 단위 테스트 (1) | 2024.12.11 |
[Canvas-불꽃놀이-25] ParticleManager 클래스 단위 테스트 (1) | 2024.12.11 |
[Canvas-불꽃놀이-24] SparkParticle 클래스 단위 테스트 (1) | 2024.12.09 |