What are unit testing strategies for D3JS?
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.
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.