在我的Jest单元测试中,我正在渲染一个ColorPicker的组件。 ColorPicker
组件创建画布对象和2d上下文但返回'undefined'
,这会引发错误"Cannot set property 'fillStyle' of undefined"
if (typeof document == 'undefined') return null; // Dont Render On Server
var canvas = document.createElement('canvas');
canvas.width = canvas.height = size * 2;
var ctx = canvas.getContext('2d'); // returns 'undefined'
ctx.fillStyle = c1; // "Cannot set property 'fillStyle' of undefined"
我遇到了麻烦,弄清楚为什么我无法获得二维背景。我的测试配置可能有问题吗?
"jest": {
"scriptPreprocessor": "<rootDir>/node_modules/babel-jest",
"unmockedModulePathPatterns": [
"<rootDir>/node_modules/react",
"<rootDir>/node_modules/react-dom",
"<rootDir>/node_modules/react-addons-test-utils",
"<rootDir>/node_modules/react-tools"
],
"moduleFileExtensions": [
"jsx",
"js",
"json",
"es6"
],
"testFileExtensions": [
"jsx"
],
"collectCoverage": true
}
答案 0 :(得分:12)
这是因为您的测试不在真实的浏览器中运行。 Jest使用jsdom
来模拟DOM的必要部分,以便能够在Node中运行测试,从而避免浏览器通常会进行的样式计算和渲染。这很酷,因为这样可以快速进行测试。
另一方面,如果您的组件中需要浏览器API,那么在浏览器中就更难了。幸运的是,jsdom
has support for canvas。你只需要配置它:
jsdom支持使用canvas包来使用canvas API扩展任何
<canvas>
元素。为了使这项工作,您需要将canvas作为依赖项包含在项目中,作为jsdom的同行。如果jsdom可以找到canvas包,它将使用它,但如果它不存在,那么<canvas>
元素将表现得像<div>
s。
或者,您可以使用Karma之类的基于浏览器的测试运行器替换Jest。无论如何,Jest是pretty buggy。
答案 1 :(得分:5)
对于那些使用create-react-app寻找示例的人
安装
yarn add --dev jest-canvas-mock
使用
创建一个新的${rootDir}/src/setupTests.js
import 'jest-canvas-mock';
答案 2 :(得分:3)
我有完全相同的问题。我部署到gitlab ci来运行我的测试,因为npm canvas需要安装Cairo,使用它不是一个可行的选择。
我真正想做的就是通过Jest模拟实现,以便它实际上并没有尝试创建真实的上下文。以下是我如何解决它:
已添加到package.json
"jest": {
"setupFiles": ["./tests/setup.js"],
}
测试/ setup.js
import sinon from 'sinon';
const createElement = global.document.createElement;
const FAKECanvasElement = {
getContext: jest.fn(() => {
return {
fillStyle: null,
fillRect: jest.fn(),
drawImage: jest.fn(),
getImageData: jest.fn(),
};
}),
};
/**
* Using Sinon to stub the createElement function call with the original method
* unless we match the 'canvas' argument. If that's the case, return the Fake
* Canvas object.
*/
sinon.stub(global.document, 'createElement')
.callsFake(createElement)
.withArgs('canvas')
.returns(FAKECanvasElement);
答案 3 :(得分:3)
对于我的用例,我做了像这样的简单猴子补丁
beforeEach(() => {
const createElement = document.createElement.bind(document);
document.createElement = (tagName) => {
if (tagName === 'canvas') {
return {
getContext: () => ({}),
measureText: () => ({})
};
}
return createElement(tagName);
};
});
无需安装canvas-prebuilt或sinon。
答案 4 :(得分:1)
要开玩笑地测试画布输出,您需要执行以下操作:
确保您至少使用jsdom13。您可以通过加入jsom包来进行开玩笑,其中14个是:
jest-environment-jsdom-fourteen
并配置玩笑以使用
jest --env=jest-environment-jsdom-fourteen
或在package.json
"jest": {
...
"testEnvironment": "jest-environment-jsdom-fourteen",
包括canvas
npm软件包。 (从2.x版本开始,此版本包含构建版本,因此不建议使用canvas预构建版本)。
答案 5 :(得分:1)
如果您使用 create-react-app,请使用 npm i --save-dev jest-canvas-mock
安装 jest-canvas-mock,并在测试文件的顶部放置 import 'jest-canvas-mock'
答案 6 :(得分:1)
如果安装了库 node-canvas,Jest / jsdom 可以处理画布元素。
因此卸载 jest-canvas-mock
(如果已安装)并安装 canvas
:
npm uninstall jest-canvas-mock
npm i --save-dev canvas
答案 7 :(得分:0)
npm install -D canvas-prebuilt@1
这为jest提供了对canvas的支持。即使由于Lottie.js而出错,这也可以使用。
答案 8 :(得分:0)
我设法通过用react-testing-library和jest-image-snapshot在画布上创建了一个图像快照测试。有点流行,但效果很好。
如果您能够使用节点画布(不是jest-canvas-mock或类似的东西)正确设置笑话测试,则可以从字面上调用canvas元素上的toDataURL。
import {render, waitForElement} from 'react-testing-library'
import React from 'react'
import { toMatchImageSnapshot } from 'jest-image-snapshot'
expect.extend({ toMatchImageSnapshot })
test('open a canvas', async () => {
const { getByTestId } = render(
<YourCanvasContainer />,
)
const canvas = await waitForElement(() =>
getByTestId('your_canvas'),
)
const img = canvas.toDataURL()
const data = img.replace(/^data:image\/\w+;base64,/, '')
const buf = Buffer.from(data, 'base64')
expect(buf).toMatchImageSnapshot({
failureThreshold: 0.001,
failureThresholdType: 'percent',
})
})
我发现我需要使用较低的阈值而不是直接比较image / png数据URL,因为在travis-CI上运行时,有两个像素只是随机不同
还可以考虑将jest环境jsdom手动升级到jest-environment-jsdom-thirteen或jest-environment-jsdom-fourteen(此页面上的其他答案类似),并参考https://github.com/jsdom/jsdom/issues/1782