簡體   English   中英

我如何使用 Mocha 對繪制在屏幕上的 React 組件進行單元測試<canvas> ?

[英]How can I use Mocha to unit-test a React component that paints on a <canvas>?

我有一個使用<canvas>進行用戶交互的 React 組件。 我沒有使用react-canvasreact-art或類似的東西; 相反,我只是在componentDidMountcomponentDidUpdate繪制畫布的 2D 上下文。

我將盡可能多的邏輯提取到兩個單獨的模塊中:一個包含完全純函數並提供獨立於 React 的核心操作,另一個提供要附加到 React 組件的事件處理程序和生命周期 mixin。 我可以輕松地測試第一個,並通過一些嘲諷來測試第二個。

但是,我還想對主畫布組件進行最低限度的測試,以確保它可以在給定一些合理的道具集的情況下無錯誤地呈現。 這證明相當困難,因為componentDidMount調用this.refs.canvas.getContext('2d') ,它似乎沒有在節點環境中定義。 所以我想出了以下解決方案,我不太喜歡它; 它涉及修補React.createElement和創建一個假的上下文對象:

// file: test/components/MyCanvasTest.jsx
import {describe, it} from 'mocha';
import {expect} from 'chai';

import React, {Component} from 'react';

import {declareMochaMock} from '../TestUtils';
import {
    renderIntoDocument,
    scryRenderedDOMComponentsWithTag,
} from 'react-addons-test-utils';

import MyCanvas from '../../src/components/MyCanvas';

describe('MyCanvas', () => {

    const noop = () => {};

    // These things are used in my component
    // but I don't want them to actually do anything,
    // so I mock them as no-ops.
    const propsToMockAsNoop = [
        'addEventListener',
        'removeEventListener',
        'setInterval',
        'clearInterval',
    ];
    propsToMockAsNoop.forEach(prop => declareMochaMock(window, prop, noop));

    // This thing is used in my component
    // and I need it to return a dummy value.
    declareMochaMock(window, 'getComputedStyle', () => ({ width: "720px" }));

    // This class replaces <canvas> elements.
    const canvasMockClassName = 'mocked-canvas-component';
    class CanvasMock extends Component {
        render() {
            return <div className={canvasMockClassName} />;
        }
        constructor() {
            super();
            this.width = 720;
            this.height = 480;
        }
        getContext() {
            // Here I have to include all the methods
            // that my component calls on the canvas context.
            return {
                arc: noop,
                beginPath: noop,
                canvas: this,
                clearRect: noop,
                fill: noop,
                fillStyle: '',
                fillText: noop,
                lineTo: noop,
                measureText: () => 100,
                moveTo: noop,
                stroke: noop,
                strokeStyle: '',
                textAlign: 'left',
                textBaseline: 'baseline',
            };
        }
    }

    const originalCreateElement = React.createElement;
    declareMochaMock(React, 'createElement', (...args) => {
        const newArgs = args[0] === 'canvas' ?
            [CanvasMock, ...args.slice(1)] :
            args;
        return originalCreateElement.apply(React, newArgs);
    });

    it("should render a <canvas>", () => {
        const element = <MyCanvas />;
        const component = renderIntoDocument(element);
        expect(scryRenderedDOMComponentsWithTag
            (component, canvasMockClassName)).to.have.length(1);
    });

});

declareMochaMock函數定義為

// file: test/TestUtils.js
export function declareMochaMock(target, propertyName, newValue) {
    let oldExisted;
    let oldValue;
    before(`set up mock for '${propertyName}'`, () => {
        oldValue = target[propertyName];
        oldExisted = Object.prototype.hasOwnProperty.call(
            target, propertyName);
        target[propertyName] = newValue;
    });
    after(`tear down mock for '${propertyName}'`, () => {
        if (oldExisted) {
            target[propertyName] = oldValue;
        } else {
            delete target[propertyName];
        }
    });
}

我無法使用淺渲染器,因為我的組件通過ref訪問畫布,而淺渲染器尚不支持ref

有沒有辦法用我當前的單元測試框架來處理這個測試(即,不添加 Jest 或類似的東西),同時減少測試工具需要知道的數量?

(完整的畫布組件可在此處獲得。)

您應該能夠使用:

https://www.npmjs.com/package/canvas#installation以及 JSDOM。 (確保您遵循操作系統的安裝程序)

示例測試:

import React from 'react';
import { mount } from 'enzyme';
import { expect } from 'chai';
import { jsdom } from 'jsdom';

import Chart from '../../src/index';

const createDOM = () => jsdom('<!doctype html><html><body><div></div></body></html>');

describe('<Chart />', () => {
  let DOM;

  const data = {
    labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
    datasets: [
      {
        label: 'My First dataset',
        backgroundColor: 'rgba(255,99,132,0.2)',
        borderColor: 'rgba(255,99,132,1)',
        borderWidth: 1,
        hoverBackgroundColor: 'rgba(255,99,132,0.4)',
        hoverBorderColor: 'rgba(255,99,132,1)',
        data: [65, 59, 80, 81, 56, 55, 40]
      }
    ]
  };

  const mountComponent = props => mount(
    <Chart data={data} {...props} />,
    { attachTo: DOM.body.firstChild }
  );

  beforeEach(() => {
    DOM = createDOM();
  });

  it('renders', () => {
    const wrapper = mountComponent();
    console.log(wrapper.html());

    expect(true).to.be.true;
  });
});

它正在測試掛載到畫布元素的 Chart.js。 我正在使用 mocha 和酶,但對於您正在使用的 w/e 測試套件,應該不會有所不同。

我在使用 AVA 測試 Lottie 動畫時遇到了這個問題。 gor181的解決方案的幫助下,這是我在 2020 年使用最新版本的JSDOM 16.2.1使其工作的方法。

首先安裝 canvas 和 JSDOM npm i canvas jsdom --save-dev

這是一個使用 JSDOM 的示例測試:

import { mount } from 'enzyme';
import { JSDOM } from 'jsdom';

const DOM = new JSDOM('<!doctype html><html><body><div></div></body></html>');
const mountComponent = props =>
    mount(<AnimationComponent options={defaultOptions} />, {
        attachTo: DOM.body
    });

test('Animation', t => {
    const wrapper = mountComponent();
    console.log(wrapper.html()); 
    t.is(wrapper.find('div').length, 2);
});

您可以在 console.log DOM 中查看您要在mountComponent附加哪些元素

console.log('DOM:', DOM);

希望這可以幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM