이전 글 : [Canvas-불꽃놀이-25] ParticleManager 클래스 단위 테스트
TextData 단위 테스트 - 사용자가 입력한 문자열을 화면에 그리고 픽셀데이터를 반환
TextData 단위 테스트
불꽃놀이 애니메이션에서 파티클을 텍스트 모양으로 렌더링 하기 위해선 text의 필셀 데이터를 정확히 추출해야 한다.
TextData 클래스는 캔버스에 사용자가 입력한 문자열을 그려 렌더링에 필요한 데이터를 가져온다.
테스트를 통해 TextData 클래스가 픽셀 데이터를 오류 없이 가져오는지 검증해 보도록 한다.
TextData 전체 코드
더보기
import CanvasOption from "@/js/CanvasOption.js";
import { FONT } from "@/js/constants.js";
class TextData extends CanvasOption {
/**
* 사용자가 입력한 Text를 캔버스에 그리고 픽셀 데이터를 생성하기 위한 클래스
* @param {string} userInput
* @param {number} fontSize
*/
constructor(userInput, fontSize) {
super();
this.text = userInput;
this.fontSize = fontSize;
this.ctx.font = this.setFontStyle(fontSize);
this.ctx.textAlign = FONT.TEXT_ALIGN;
this.ctx.textBaseline = FONT.TEXT_BASELINE;
this.ctx.strokeStyle = FONT.TEXT_COLOR;
this.textPixelData = {};
}
drawText() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.strokeText(this.text, this.mainX, this.mainY);
const { width, fontBoundingBoxAscent = 0, fontBoundingBoxDescent = 0 } = this.ctx.measureText(this.text);
const x = (this.mainX - width / 2) * this.dpr;
const y = (this.mainY - this.fontSize / 2 - fontBoundingBoxAscent) * this.dpr;
const imgWidth = width * this.dpr;
const imgHeight = (this.fontSize + fontBoundingBoxDescent) * this.dpr;
if (width) {
this.textPixelData = this.ctx.getImageData(x, y, imgWidth, imgHeight);
this.textPixelData.fontBoundingBoxAscent = fontBoundingBoxAscent;
this.textPixelData.fontBoundingBoxDescent = fontBoundingBoxDescent;
}
}
}
export default TextData;
1. 공통 테스트 환경 설정
- 매번 테스트 전에 TextData 클래스의 인스턴스를 생성해 모든 테스트에서 공통적으로 사용하도록 한다.
- CanvasOption을 상속받으므로 dom에 canvas 태그를 추가하고 jest-canvas-mock과 같은 환경이 필요하다. 이에 setup.js 파일에 테스트용 캔버스를 생성하고 크기를 설정하는 공통 함수를 생성한다.
// constants.js
export const TEST_OPTION = {
// 생략...
CANVAS_ELEMENT: '<canvas id="canvas"></canvas>',
CANVAS_ID: "canvas",
};
// setup.js
const { CANVAS_ELEMENT, TYPE_INNER_WIDTH, TYPE_INNER_HEIGHT, INNER_WIDTH, INNER_HEIGHT } = TEST_OPTION;
export const setTestCanvas = () => {
// 테스트용 캔버스 추가 (innerHTML로 삽입해야 테스트마다 캔버스가 초기화됨)
document.body.innerHTML = CANVAS_ELEMENT;
// window객체의 innerWidth, innerHeight 커스텀으로 설정
defineDomObjectProperty({ domObj: window, property: TYPE_INNER_WIDTH, value: INNER_WIDTH });
defineDomObjectProperty({ domObj: window, property: TYPE_INNER_HEIGHT, value: INNER_HEIGHT });
};
// TextData.test.js
describe("TextData 클래스 단위 테스트", () => {
// test에서 사용할수 있게 전역에서 선언
let textData;
// TextData 인스턴스를 만들때 사용할 데이터
const [userInput, fontSize] = ["TextData 테스트", 100];
const { TYPE_INNER_WIDTH, TYPE_INNER_HEIGHT, INNER_WIDTH, INNER_HEIGHT } = TEST_OPTION;
beforeEach(() => {
// 테스트용 캔버스 생성과 설정
setTestCanvas();
// TextData 인스턴스 생성
textData = new TextData(userInput, fontSize);
// canvas와 상호 연결되어있지 않으므로 canvas의 물리적 크기를 직접 지정해준다.
textData.canvas.width = INNER_WIDTH * textData.dpr;
textData.canvas.height = INNER_HEIGHT * textData.dpr;
});
// 생략...
}
2. TextData 클래스 생성자와 멤버변수 초기화
- 인스턴스 생성 시 모든 멤버 변수와 ctx 속성이 예측한 값으로 올바르게 초기화되는지 확인한다.
// TextData.test.js
test("TextData 생성자와 멤버변수 초기화", () => {
// 멤버변수 초기화 확인
expect(textData.text).toBe(userInput);
expect(textData.fontSize).toBe(fontSize);
expect(textData.textPixelData).toEqual({});
// ctx 속성 확인
expect(textData.ctx.font).toBe(`${fontSize}px "${FONT.FAMILY}"`);
expect(textData.ctx.textAlign).toBe(FONT.TEXT_ALIGN);
expect(textData.ctx.textBaseline).toBe(FONT.TEXT_BASELINE);
expect(textData.ctx.strokeStyle).toBe(FONT.TEXT_COLOR);
});
3. drawText 테스트 - 텍스트를 화면에 그려 픽셀 데이터를 가져오는지
- drawText를 실행하기 전에 가져오는 measureText, imageData 등 데이터에 대한 mock 처리를 해준다.
- drawText 구성하고 있는 메서드의 실행여부를 검증한다.
- mock처리한 텍스트의 imageData가 실제로 잘 반환을 하는지 확인한다.
- measureText를 통해 가져오는 텍스트의 사이즈 정보에 대해 기본값이 잘 적용되는지 확인한다.
// TextData.test.js
test.each([
{ width: 200, fontBoundingBoxAscent: 20, fontBoundingBoxDescent: 20 },
{ width: 200, fontBoundingBoxAscent: undefined, fontBoundingBoxDescent: undefined },
])(
"TextData 클래스 drawText - 텍스트를 그리고 픽셀데이터 추출 (fontBoundingBoxAscent,fontBoundingBoxDescent: $fontBoundingBoxAscent일때)",
(measureTextMockData) => {
// measureText mock 함수화하고 반환값 지정
jest.spyOn(textData.ctx, "measureText").mockReturnValue(measureTextMockData);
// getImageData 실행에 필요한 좌표값과, 영역 예측값 설정
const { width, fontBoundingBoxAscent = 0, fontBoundingBoxDescent = 0 } = measureTextMockData;
const expectedImgX = (textData.mainX - width / 2) * textData.dpr;
const expectedImgY = (textData.mainY - fontSize / 2 - fontBoundingBoxAscent) * textData.dpr;
const expectedImgWidth = width * textData.dpr;
const expectedImgHeight = (fontSize + fontBoundingBoxDescent) * textData.dpr;
// getImageData를 mock 함수화하고 반환값을 설정
const getImageDataMockData = {
data: new Uint8ClampedArray(100), // 더미 데이터
width: expectedImgWidth,
height: expectedImgHeight,
};
jest.spyOn(textData.ctx, "getImageData").mockReturnValue(getImageDataMockData);
textData.drawText();
// drawText를 구성하고있는 메서드들의 호출여부 검증
expect(textData.ctx.clearRect).toHaveBeenCalledWith(0, 0, textData.canvas.width, textData.canvas.height);
expect(textData.ctx.strokeText).toHaveBeenCalledWith(userInput, textData.mainX, textData.mainY);
expect(textData.ctx.measureText).toHaveBeenCalledWith(userInput);
// getImageData를 호출 후 반환값이 예측값과 일치하는지 검증
expect(textData.ctx.getImageData).toHaveBeenCalledWith(expectedImgX, expectedImgY, expectedImgWidth, expectedImgHeight);
expect(textData.textPixelData).toBeDefined();
expect(textData.textPixelData.data).toBeInstanceOf(Uint8ClampedArray);
expect(textData.textPixelData).toEqual(getImageDataMockData);
},
);
테스트 결과
npx jest ./__test__/particle/TextData.test.js
Github Repo
'Canvas > 불꽃놀이 프로젝트' 카테고리의 다른 글
[Canvas-불꽃놀이-28] Canvas 클래스 단위 테스트 (초기화) (1) | 2024.12.17 |
---|---|
[Canvas-불꽃놀이-27] CanvasOption 클래스 단위 테스트 (1) | 2024.12.17 |
[Canvas-불꽃놀이-25] ParticleManager 클래스 단위 테스트 (1) | 2024.12.11 |
[Canvas-불꽃놀이-24] SparkParticle 클래스 단위 테스트 (1) | 2024.12.09 |
[Canvas-불꽃놀이-23] CircleParticle 클래스 단위 테스트 (1) | 2024.12.09 |