이전 글 : [Canvas-불꽃놀이-27] CanvasOption 클래스 단위 테스트
Canvas 클래스 - 생성자와 멤버변수 초기화 검증
Canvas 클래스 초기화 관련 테스트
Canvas 클래스는 파티클 생성, 업데이트, 렌더등 애니메이션을 총괄하는 로직을 담고 있으므로 테스트 범위가 크다.
이에 따라 각 파트별로 나누어 테스트를 진행한다.
먼저 이 글에서는 생성자와 멤버변수 초기화, 초기화와 관련된 메서드를 테스트하겠다.
Canvas 클래스 초기화 관련 코드
더보기
class Canvas extends CanvasOption {
/**
* 애니메이션을 실행할 캔버스 클래스
* - 화면에 불꽃놀이와 텍스트 파티클을 그리기 위한 기능을 제공
* - CanvasOption을 확장하여 캔버스의 다양한 설정을 관리
*/
constructor() {
super();
this.initCanvasVars();
}
init() {
super.init();
this.canvas.width = this.canvasCssWidth * this.dpr;
this.canvas.height = this.canvasCssHeight * this.dpr;
this.ctx.scale(this.dpr, this.dpr);
this.canvas.style.width = `${this.canvasCssWidth}px`;
this.canvas.style.height = `${this.canvasCssHeight}px`;
this.mainTailVY = this.calculateTailVY(this.mainY);
this.createTailPosX();
this.createTailVY();
this.pm.isSmallScreen = this.isSmallScreen;
this.pm.maxPoolSize[TYPE_TEXT] *= this.textLength;
this.pm.maxPoolSize[TYPE_SPARK] *= this.textLength;
}
initCanvasVars() {
this.animationId = undefined;
this.text = "";
this.textLength = 0;
this.mainTextData = {};
this.subTextData = {};
this.tailQty = TAIL.BASE_QTY;
this.mainTailVY = 0;
this.tailsLeftPosX = [];
this.tailsRightPosX = [];
this.tailsVY = [];
this.tailCount = 0;
this.isLeft = false;
this.tailParticles = [];
this.textParticles = [];
this.circleParticles = [];
this.sparkParticles = [];
this.pm = new ParticleManager(this.ctx, this.isSmallScreen);
}
createTailPosX() {
const length = this.tailQty;
const exclusionDist = this.isSmallScreen ? TAIL.SMALL_EXCLUSION : TAIL.EXCLUSION;
const leftStart = this.canvasCssWidth * TAIL.START_X_RATIO;
const leftEnd = this.mainX - exclusionDist;
const rightStart = this.mainX + exclusionDist;
const xOffset = (leftEnd - leftStart) / (length - 1);
this.tailsLeftPosX = Array.from({ length }, (_, i) => Math.floor(leftStart + i * xOffset));
this.tailsRightPosX = Array.from({ length }, (_, i) => Math.floor(rightStart + i * xOffset));
}
/**
* @param {number} yPos
* @returns {number} y좌표를 인자로 받아 y의 속도를 반환
*/
calculateTailVY(yPos) {
return (yPos - this.canvasCssHeight) / this.interval;
}
createTailVY() {
const length = this.tailQty;
const minTailVY = this.calculateTailVY(this.canvasCssHeight * TAIL.MIN_Y_RATIO);
const maxTailVY = this.calculateTailVY(this.canvasCssHeight * TAIL.MAX_Y_RATIO);
const vyOffset = (maxTailVY - minTailVY) / (length - 1);
this.tailsVY = Array.from({ length }, (_, i) => minTailVY + i * vyOffset);
}
// 생략....
}
1. 테스트 환경 공통 설정
- Canvas 클래스의 인스턴스, 사용자 입력 문자열, 메서드 호출을 감시할 변수는 전역에서 선언하여 사용하도록 한다.
// CanvasInitialize.test.js
describe("Canvas 클래스 초기화 테스트", () => {
let canvasInst;
const userInput = "Canvas 초기화";
const { INNER_WIDTH, INNER_HEIGHT } = TEST_OPTION;
let spyInitCanvasVars;
// 생략...
}
- 테스트를 하다 보면 초기 설정과 달라질 수 있으므로 테스트 전 캔버스를 다시 세팅한다. 인스턴스 생성과 공통된 속성(문자열, 문자열 길이 등)을 추가하고 호출을 감시할 메서드도 정의하도록 한다.
- 특정 메서드의 스파이를 생성하고 사용하면 호출 기록이 쌓이니 테스트 후 호출 기록을 삭제하도록 해 다음 테스트에 영향이 가지 않도록 한다.
// CanvasInitialize.test.js
// 테스트 전
beforeEach(() => {
// 테스트트용 캔버스 생성과 크기 설정
setTestCanvas();
// 호출을 감시할 스파이 생성
spyInitCanvasVars = jest.spyOn(Canvas.prototype, "initCanvasVars");
// canvas 인스턴스와 공통 속성 설정
canvasInst = new Canvas();
canvasInst.text = userInput;
canvasInst.textLength = userInput.length;
canvasInst.isSmallScreen = false;
});
// 테스트 후 등록한 스파이의 호출 기록 일괄적으로 초기화
afterEach(() => {
jest.clearAllMocks();
});
2. 생성자와 initCanvasVars 테스트
- 생성자와 생성자 안에서 실행되는 initCanvasVars를 테스트하여 멤버변수의 초기화가 잘 이루어지는지 확인하도록 한다.
// CanvasInitialize.test.js
test("constructor, initCanvasVars 테스트 | 생성자와 멤버변수 초기화 테스트", () => {
// canvas 인스턴스를 생성하기 전 스파이를 등록해서 생성자 안에있는 메서드 호출을 감시할 수 있다.
// 1번 실행되었는지 확인
expect(spyInitCanvasVars).toHaveBeenCalledTimes(1);
// 멤버변수 초기화 값이 예상값과 일치하는지 검증
expect(canvasInst.animationId).toBeUndefined();
expect(canvasInst.text).toBe(userInput);
expect(canvasInst.textLength).toBe(userInput.length);
expect(canvasInst.mainTextData).toEqual({});
expect(canvasInst.subTextData).toEqual({});
expect(canvasInst.tailQty).toBe(TAIL.BASE_QTY);
expect(canvasInst.mainTailVY).toBe(0);
expect(canvasInst.tailsLeftPosX).toHaveLength(0);
expect(canvasInst.tailsRightPosX).toHaveLength(0);
expect(canvasInst.tailsVY).toHaveLength(0);
expect(canvasInst.tailCount).toBe(0);
expect(canvasInst.isLeft).toBeFalsy();
expect(canvasInst.tailParticles).toHaveLength(0);
expect(canvasInst.textParticles).toHaveLength(0);
expect(canvasInst.circleParticles).toHaveLength(0);
expect(canvasInst.sparkParticles).toHaveLength(0);
expect(canvasInst.pm).toBeInstanceOf(ParticleManager);
});
3. init 메서드 테스트
- Canvas의 init 메서드는 DPR에 따른 캔버스 물리적 크기를 설정하고 애니메이션에 필요한 멤버변수의 값을 계산하는 역할을 한다.
- 또한 화면이 리사이즈되어 다시 로드를 할 때 화면의 크기가 바뀌니 애니메이션에 필요한 시작값들을 재계산하는 역할도 한다.
- tail의 x 좌표값 배열과 y 좌표값 속도 배열을 계산하는 메서드는 간단히 호출여부만 확인하도록 하고 별도의 테스트로 분리해서 진행하도록 한다.
// CanvasInitiallize.test.js
test("init 테스트", () => {
// init 메서드 실행전 tail 관련 메서드의 호출을 감시하도록 spy 등록
const spyCreateTailPosX = jest.spyOn(canvasInst, "createTailPosX");
const spyCreateTailVY = jest.spyOn(canvasInst, "createTailVY");
canvasInst.init();
// 캔버스의 물리적 크기와 렌더링 시 보여줄 캔버스 태그 크기 검증
expect(canvasInst.canvas.width).toBe(INNER_WIDTH * canvasInst.dpr);
expect(canvasInst.canvas.height).toBe(INNER_HEIGHT * canvasInst.dpr);
expect(canvasInst.ctx.scale).toHaveBeenCalledWith(canvasInst.dpr, canvasInst.dpr);
expect(canvasInst.canvas.style.width).toBe(`${INNER_WIDTH}px`);
expect(canvasInst.canvas.style.height).toBe(`${INNER_HEIGHT}px`);
// 꼬리를 생성하기 위해 필요한 값들 검증 (메서드는 별도의 테스트로 분리한다. 간단히 호출 여부만 확인)
expect(canvasInst.mainTailVY).toBeCloseTo((canvasInst.mainY - INNER_HEIGHT) / canvasInst.interval);
expect(canvasInst.tailsLeftPosX).toBeDefined();
expect(canvasInst.tailsRightPosX).toBeDefined();
expect(canvasInst.tailsLeftPosX).toHaveLength(TAIL.BASE_QTY);
expect(canvasInst.tailsRightPosX).toHaveLength(TAIL.BASE_QTY);
expect(canvasInst.createTailPosX).toHaveBeenCalledTimes(1);
expect(canvasInst.createTailVY).toHaveBeenCalledTimes(1);
// 파티클의 생성과 반납을 관리하는 Particle 매니저 속성과 풀 사이즈 계산값 검증
expect(canvasInst.pm.isSmallScreen).toBeFalsy();
expect(canvasInst.pm.maxPoolSize[PARTICLE.TYPE_TEXT]).toBe(PARTICLE.TEXT_POOL * canvasInst.textLength);
expect(canvasInst.pm.maxPoolSize[PARTICLE.TYPE_SPARK]).toBe(PARTICLE.SPARK_POOL * canvasInst.textLength);
});
4. Tail 생성 관련 테스트
- Tail의 생성 위치를 정해 그 안에서만 생성되도록 하면 시각적으로 균형 있는 모습을 볼 수 있다.
이에 화면(모바일/PC) width에 따라 왼쪽, 중앙, 오른쪽에 생성될 tail의 x좌표값들의 배열을 검증하도록 한다. - 모바일 화면은 width가 좁아지므로 제외 거리가 다른 비율로 계산되기 때문에 화면 크기별로 테스트를 해야 한다.
// CanvasInitialize.test.js
test.each([
// 두가지 환경으로 나누어 진행
{ isSmallScreen: true, notice: "모바일 화면" },
{ isSmallScreen: false, notice: "PC 화면" },
])("createTailPosX 테스트 | 멤버변수 tailsLeftPosX, tailsRightPosX 검증, $notice", ({ isSmallScreen }) => {
canvasInst.isSmallScreen = isSmallScreen;
canvasInst.createTailPosX();
// 배열값들이 정의되었는지 확인하고 배열의 길이가 예상값과 일치하는지 확인한다.
expect(canvasInst.tailsLeftPosX).toBeDefined();
expect(canvasInst.tailsRightPosX).toBeDefined();
expect(canvasInst.tailsLeftPosX).toHaveLength(canvasInst.tailQty);
expect(canvasInst.tailsRightPosX).toHaveLength(canvasInst.tailQty);
// 배열은 일정한 규칙성을 띄므로 중앙에서의 제외 거리를 구해 왼쪽의 마지막 값, 오른쪽의 첫번째 값을 검증하도록 한다.
const expectedExclusionDist = canvasInst.isSmallScreen ? TAIL.SMALL_EXCLUSION : TAIL.EXCLUSION;
expect(canvasInst.tailsLeftPosX.at(-1)).toBe(canvasInst.mainX - expectedExclusionDist);
expect(canvasInst.tailsRightPosX.at(0)).toBe(canvasInst.mainX + expectedExclusionDist);
});
- 맨 아래에서부터 출발하니 y좌표의 위치는 canvas의 height다.
어느 정도의 속도로 움직일지 정해야 하니 tail의 y좌표의 속도 배열을 검증한다.
// CanvasIniaialize.test.js
test("createTailVY 테스트 | tail의 y 속도 배열 계산", () => {
canvasInst.createTailVY();
// y 속도값 배열의 정의되었는지와 예상길이가 일치한지, 배열의 첫번째 원소가 예상값과 일치한지 검증하도록 한다.
expect(canvasInst.tailsVY).toBeDefined();
expect(canvasInst.tailsVY).toHaveLength(TAIL.BASE_QTY);
expect(canvasInst.tailsVY[0]).toBe(canvasInst.calculateTailVY(INNER_HEIGHT * TAIL.MIN_Y_RATIO));
});
테스트 결과
npx jest .__test__/canvas/CanvasInitialize.test.js
Github Repo
'Canvas > 불꽃놀이 프로젝트' 카테고리의 다른 글
[Canvas-불꽃놀이-30] Canvas 클래스 단위 테스트 (파티클 생성) (1) | 2024.12.19 |
---|---|
[Canvas-불꽃놀이-29] Canvas 클래스 단위 테스트 (텍스트 데이터 생성) (1) | 2024.12.17 |
[Canvas-불꽃놀이-27] CanvasOption 클래스 단위 테스트 (1) | 2024.12.17 |
[Canvas-불꽃놀이-26] TextData 클래스 단위 테스트 (1) | 2024.12.11 |
[Canvas-불꽃놀이-25] ParticleManager 클래스 단위 테스트 (1) | 2024.12.11 |