Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3js in a React Component Unit Testing using Jest/Enzyme

Problem: When I run my code, the SVG appends to the outer div on the component and renders on the page perfectly. However, during testing, the SVG is not appended to the outer div.

Stack: I am trying to test my d3js element wrapped in a React component using Jest/Enzyme.

Research I did so far: Looked up a bunch of guides/posts on testing d3js using google and on stackoverflow. Used a guide on unit testing d3js and React with Jasmine. I have also read that testing 3rd party libraries may be difficult with Enzyme so this may be a limitation of the testing framework.

I have included a basic sample of code to test.

Component:

import React from "react";
import * as d3 from "d3";

export default class Circle extends React.Component {

    componentDidMount = () => {
        return this.renderCircle();
    }

    renderCircle = () => {
        d3.select("#outerDiv")
            .append("svg")
            .attr("width", 600)
            .attr("height", 300)
            .append("circle")
            .attr("id", "d3-el")
            .attr("cx", 300)
            .attr("cy", 150)
            .attr("r", 30)
            .attr("fill", "#ff0000")
            .on("click", function (d, i) {
                var item = d3.select(this);
                item.attr("fill", "#00ff00");
            });
    }

    render() {
        return <div id="outerDiv" />;
    }

}

Test:

import React from "react";
import * as d3 from "d3";
const jsdom = require("jsdom");
const ReactJSDOM = require('react-jsdom');
const { JSDOM } = jsdom;
import Circle from "./Circle.js";
import { mount } from "enzyme";
import sinon from 'sinon';

describe("Circle", () => {
    it("d3js element should be appended to outer div", () => {
        let wrapper = mount(<Circle />);
        let instance = wrapper.instance();
        const jsdomElem = ReactJSDOM.render(<html><body><p><Circle /></p></body></html>);
        console.log(jsdomElem.querySelector("#d3-el")); // returns null
        console.log(wrapper.find("d3-ele").get(0));; // returns undefined
        console.log(d3.select("d3-el")); // returns null
        expect(wrapper.find("d3-el").exists()).toEqual(true);
    }
}

Doing a find on the d3 element that is supposed to be rendered using wrapper.find(), document.querySelector(), and d3.select() all return undefined or null.

Things I have tried but does not append the SVG element to the outer div:

  1. Using mount() on the component.

  2. Using instance() of the mounted component and calling the renderCircle() method directly.

  3. Using ReactJSDOM to render a whole DOM from top to bottom.

  4. A bunch of other stuff that probably makes no sense including sinon.spy() and mocking a jest.fn().

None of these methods work and if I do a find on the outer div, it does not show that the div has any children elements. Doing a debug on the wrapper shows the same thing.

Would love some help to get this SVG appended to the div during testing. Any solutions or other strategies would be greatly appreciated. Please let me know if you have any questions or if I should add more information. Thanks!

like image 260
Ken Avatar asked Mar 11 '19 14:03

Ken


1 Answers

First things first, you should separate concerns of your components.

Move d3-related code into separate function (or class) and test your component as you would test any other React component.

After that, test D3-related functionality. Take a look at tests for D3 library itself, for example here. They use jsdom to simulate behavior of real DOM.

like image 170
eagle.dan.1349 Avatar answered Sep 24 '22 03:09

eagle.dan.1349