如何在 Jest 中为我的测试添加 <canvas> 支持?

IT技术 reactjs html5-canvas jsdom jestjs
2021-04-06 20:29:29

在我的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"

我无法弄清楚为什么我无法获得 2d 上下文。也许我的测试配置有问题?

"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
}
6个回答

对于那些使用 create-react-app 寻找示例的人

安装

yarn add --dev jest-canvas-mock

创建一个新${rootDir}/src/setupTests.js

import 'jest-canvas-mock';
是什么导入了这个神秘的新setupTests.js文件?它是如何神奇地使用的?
2021-05-27 20:29:29
create react app 没有 jest.config.js 文件,我们需要创建它吗?以及当我们添加 setupFiles: [ "jest-canvas-mock" ] 时它应该是什么样子?
2021-06-05 20:29:29
就我而言,我添加jest-canvas-mock了我jest.config.js喜欢的setupFiles: [ "jest-canvas-mock" ]
2021-06-06 20:29:29

这是因为您的测试不在真正的浏览器中运行。Jestjsdom用于模拟 DOM 的必要部分,以便能够在 Node 中运行测试,从而避免浏览器通常会执行的样式计算和渲染。这很酷,因为这可以加快测试速度。

另一方面,如果您需要在组件中使用浏览器 API,这比在浏览器中更困难。幸运的是,jsdom支持 canvas你只需要配置它:

jsdom 支持使用画布包来扩展任何<canvas>带有画布 API 的元素。要完成这项工作,您需要将 canvas 作为依赖项包含在您的项目中,作为 jsdom 的对等项。如果 jsdom 可以找到 canvas 包,它将使用它,但如果它不存在,则<canvas>元素的行为类似于<div>s。

或者,您可以将 Jest 替换为一些基于浏览器的测试运行程序,例如Karma无论如何,Jest 是相当有缺陷的。

如果有人来寻找解决方案,Jest 附带了一个非常旧版本的jsdom. 如果您添加jsdom到您的依赖项并将解决方案添加到"jest/**/jsdom": "13.0.0",这应该可以解决它。
2021-05-23 20:29:29
旧问题/答案,但从那时起 Jest 有了很大改进,并且要让画布与 jsdom(在大多数系统上)一起工作,您需要做的就是npm install canvas-prebuilt- npmjs.com/package/canvas-prebuilt
2021-06-07 20:29:29
我正在转向业力。安装画布包依赖项对于其他人来说太麻烦了。谢谢!
2021-06-12 20:29:29
我也安装了画布,即使在那个玩笑未能检测到这个画布元素之后,我也只能根据项目要求使用玩笑...你能帮我吗,即使我试过用画布预建...同样的问题。请帮忙
2021-06-14 20:29:29
@ChidG 谢谢你提供的信息。普通的画布包不可能在 Windows 10 上构建,至少对我来说......
2021-06-20 20:29:29

如果安装了库节点画布, Jest / jsdom 可以处理画布元素

因此卸载jest-canvas-mock(如果已安装)并安装canvas

npm uninstall jest-canvas-mock
npm i --save-dev canvas
我最初尝试使用 jest-canvas-mock,但这真的很有帮助。
2021-05-25 20:29:29
这是 create-react-app/react-scripts 4+ 的最佳答案
2021-05-26 20:29:29
花了很长时间才找到一个干净的解决方案 - jest-canvas-mock 对我不起作用,但这个答案起作用了。使用 react-scripts 4.0.3 创建反应应用
2021-06-13 20:29:29
它适用于我的 create-react-app。
2021-06-15 20:29:29

对于我的用例,我做了这样的简单猴子补丁

beforeEach(() => {
    const createElement = document.createElement.bind(document);
    document.createElement = (tagName) => {
        if (tagName === 'canvas') {
            return {
                getContext: () => ({}),
                measureText: () => ({})
            };
        }
        return createElement(tagName);
    };
});

无需安装 canvas-prebuilt 或 sinon。

哦,几个月后找到了我自己的评论
2021-05-22 20:29:29
类型错误:ctx.measureText 不是函数
2021-06-07 20:29:29

我有完全相同的问题。我正在部署到 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);