What are unit testing strategies for D3JS?

前端 未结 4 1332
后悔当初
后悔当初 2020-12-23 14:46

I am completely new to D3JS and would like to understand the testing strategies for D3 JS.

To elaborate little more on question - consider I have a simple page that

相关标签:
4条回答
  • 2020-12-23 14:58

    I think I got the answer to my own question. Will try to explain it here.

    It is not possible to validate whether the graph is plotted correctly by JS function written using D3JS. For this we may have to use Phantom.js or similar framework as mentioned by Chrisopher. I was not worried about making sure D3JS is plotting graph correctly, as any ways it is D3JS functionality and my code can safely assume D3JS is doing its work.

    My worry is more of whether the data passed to D3JS is correct and as per my requirement. It is very much possible to make sure the properties of the graph are set correctly by creating Spy objects. I am providing a sample unit test covering test cases for a JS code plotting a Circle using D3JS.

    CircleManager.js

    function CircleManager() {};
    
    CircleManager.prototype.draw = function(radius) {
        var svg = d3.select("body")
                .append("svg");
    
        svg.attr("width", 100)
                .attr("height", 100);    
    
        var circle = svg.append("circle");
        circle.style("stroke", "black")
            .style("fill", "white")
            .attr("r", radius)
            .attr("cx", 50)
            .attr("cy", 50);
    };
    

    CircleManagerSpec.js

    describe("draw", function() {
        it("Constructs an svg", function() {
            var d3SpyObject = jasmine.createSpyObj(d3, ['append', 'attr']);
    
            // Returns d3SpyObject when d3.select method is called
            spyOn(d3, 'select').andReturn(d3SpyObject);
    
            var svgSpyObject = jasmine.createSpyObj('svg', ['append', 'attr', 'style']);
    
            // Returns svgSpyObject when d3.select.append is called.
            d3SpyObject.append.andReturn(svgSpyObject);
    
    
            d3SpyObject.attr.andCallFake(function(key, value) {
                return this;
            });
    
            svgSpyObject.append.andReturn(svgSpyObject);
    
            svgSpyObject.attr.andCallFake(function(key, value) {
                return this;
            });
    
            svgSpyObject.style.andCallFake(function(key, value) {
                return this;
            });
    
            var circleManager = new CircleManager();
            circleManager.draw(50);
            expect(d3.select).toHaveBeenCalledWith('body');
            expect(d3SpyObject.append).toHaveBeenCalledWith('svg');
            expect(svgSpyObject.attr).toHaveBeenCalledWith('r', 50);
            expect(svgSpyObject.attr).toHaveBeenCalledWith('width', 100);
            expect(svgSpyObject.attr).toHaveBeenCalledWith('height', 100);
            expect(svgSpyObject.style).toHaveBeenCalledWith('stroke', 'black');
            expect(svgSpyObject.style).toHaveBeenCalledWith('fill', 'white');
        });
    });
    

    Hope this helps.

    0 讨论(0)
  • 2020-12-23 15:00

    I think you should consider this: http://busypeoples.github.io/post/testing-d3-with-jasmine/

    And it really seems to make sense. I have read the others' answers but I am little bit disagree with them. I think we not only check if right function is called or not but we can check much more than that. Checking only some function call are good at unit testing level but not enough. Such test cases written by developer will be based on developer's understanding like these functions are called or not. But whether these methods should be called or not, this thing can be only checked by going at another level because unlike other work, here are code is making something not returning something that can be just checked and make sure everything is correct.

    We obviously don't need to check whether D3 is doing its work correctly or not. So we can use D3 inside our testing code. But D3 renders SVG and we can check things like if svg have elements where expected. Again it is not going to test whether SVG is showing and rendering properly or not. We are going to check if SVG have elements which are expected and they are set as expected.

    For example: If this is bar chart, we can check the number of bars. As in example in above link here it is check that.

        // extend beforeEach to load the correct data...
    beforeEach(function() {
        var testData =  [{ date: '2014-01', value: 100}, { date: '2014-02', value: 140}, {date: '2014-03', value: 215}];
        c = barChart();
        c.setData(testData);
        c.render();
    });
    
    describe('create bars' ,function() {
        it('should render the correct number of bars', function() {
            expect(getBars().length).toBe(3);
        });
    
        it('should render the bars with correct height', function() {
            expect(d3.select(getBars()[0]).attr('height')).toBeCloseTo(420);
        });
    
        it('should render the bars with correct x', function() {
            expect(d3.select(getBars()[0]).attr('x')).toBeCloseTo(9);
        });
    
        it('should render the bars with correct y', function() {
            expect(d3.select(getBars()[0]).attr('y')).toBeCloseTo(0);
        });
    });
    
    // added a simple helper method for finding the bars..
    function getBars() {
        return d3.selectAll('rect.bar')[0];
    }
    

    Some people probably gonna say that we are going to use D3 inside testing code? Again we should remember that purpose of test writing here is not to test D3 but our logic and SVG code that is compiled in response to our code.

    This is just a way and jasmine is something that is helping us in writing test, you can also go into more detail and in different scenarios. You can make domain and check datapoints width height to cross check if they result into data which were given to render.

    I think I am clear if not then check this link : http://busypeoples.github.io/post/testing-d3-with-jasmine/

    Here write of this article have explained things in detail with how you can use jasmine. Also I think I am still gone into detail. If only unit testing is required at different js functions level then there are a lot more things which can be tested without going into elements detail.

    0 讨论(0)
  • 2020-12-23 15:08

    Testing strategy

    The strategy I end up using to test d3.js code is to create helper functions to manage my data and settings. I then unit test these functions. So for charts I would check every functionality dealing with data, every function to set width, legends etc...

    Concerning drawing functions it can get trickier but with testing frameworks such as buster.js it can be quite easy to implement these too. A good way of testing a chart would be to count the number of bars/lines in the page, check that legends are printing etc.

    I would not try to check that the chart is visually the same because visually checking that the end result is the same is easiest. However, when writing the drawing functions, one should be very attentive to what happens on updates (will changing the data draw twice as many lines? are selectors right? ...)


    Javascript testing

    A great book on javascript testing is: Test Driven Javascript Development. It provides lots of examples and strategies to test javascript code. Most of them can be directly applied to d3.js code.


    Tools

    I recently looked for solutions for unit testing d3.js code and I ended up using the following tools:

    buster.js

    Buster.js is a very complete framework for unit testing javascript code in multiple browsers.

    phantom.js

    Phantom.js is a headless WebKit scriptable with a JavaScript API.

    This means that it makes it easy to run automated tests on javascript without needing to use browsers such as chrome, safari etc..


    EDIT: I would now use jasmine for unit testing and selenium (through the Saucelabs service maybe) for end to end testing.

    0 讨论(0)
  • 2020-12-23 15:20

    Probably worth mentioning Jest snapshot testing. Jest/snapshots are popular for React UI components, as they're also difficult to test. With D3 you could generate an SVG take a snapshot and verify as your codebase evolves that you continue to generate the same output.

    function makeChart(id, dataset) {
      const chart = d3.select(id)
          .append("svg")
          .selectAll("circle")
          .data(dataset)
          .enter().append("circle")
          .style("stroke", "gray")
          .style("fill", "black")
          .attr("r", 40)
          .attr("cx", 50)
          .attr("cy", 20);
      return chart;
    }
    
    it('renders correctly', () => {
      const myChart = makeChart('#example', [1,2,3])
      expect(myChart.node()).toMatchSnapshot();
    });
    

    This test is untested

    A snapshot can be any output a string "Foo Bar" a JSON object {foo: "bar"} etc.

    Basically you have to run a test once that contains .toMatchSnapshot. Jest will then manage the generation and store a copy that it'll compare in future tests.

    The concept is similar to VCR in Ruby testing. Record and replay.

    0 讨论(0)
提交回复
热议问题