最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Testing Mouseover event in D3 with Sinon - Stack Overflow

programmeradmin6浏览0评论

I'm having trouble trying to get my test to pass. I would like to be able to use a spy to check whether or not the mouseover event was correctly called. Currently I am getting the following error, "Error: expected on to have been called at least once but was never called". Part of the confusion on my part relates to the differences between d3 and jQuery selectors, I'm more than happy to use the latter, but I'm not sure how to correctly use the former in a test to get the result I want.

My dependencies are d3, jQuery, mocha, chai, sinon, and sinon-chai.

Relevant code from my index.html file,

<script src="fixtures.js"></script>
<div id="mocha"></div>
<script src="mocha.js"></script>
<script src="chai.js"></script>
<script src="sinon-chai.js"></script>
<script src="sinon-1.10.2.js"></script>
<script>
    mocha.ui('bdd');
    mocha.reporter('html');
    var expect = chai.expect;
 </script>
 <script src="tests.js"></script>
 <script>
    mocha.run();
</script>

fixtures.js,

var path = svg.selectAll("path")
          .data(pie(data))
          .enter().append("path").attr("class", "path")
          .style("fill", function(d, i) { return color(i); })
          .attr("d", arc)
          .on("mouseover", function() { d3.select(this).style("fill", "#ff0000"); })
          .on("mouseout" , function() { d3.selectAll("path").style("fill", function(d, i) { return color(i); }); });

//  I wanted to test my understanding of d3 selectors
var path_one = d3.select('.path').attr("id", "path_one"); 

tests.js,

describe('Donut Chart', function() {
    describe("Mouseover events", function() {
        it("should call on('mouseover')", function () {
            var spy = sinon.spy(path, "on");
            $('path').mouseenter();
            sinon.assert.called(spy);
        });
    });
});

I'm having trouble trying to get my test to pass. I would like to be able to use a spy to check whether or not the mouseover event was correctly called. Currently I am getting the following error, "Error: expected on to have been called at least once but was never called". Part of the confusion on my part relates to the differences between d3 and jQuery selectors, I'm more than happy to use the latter, but I'm not sure how to correctly use the former in a test to get the result I want.

My dependencies are d3, jQuery, mocha, chai, sinon, and sinon-chai.

Relevant code from my index.html file,

<script src="fixtures.js"></script>
<div id="mocha"></div>
<script src="mocha.js"></script>
<script src="chai.js"></script>
<script src="sinon-chai.js"></script>
<script src="sinon-1.10.2.js"></script>
<script>
    mocha.ui('bdd');
    mocha.reporter('html');
    var expect = chai.expect;
 </script>
 <script src="tests.js"></script>
 <script>
    mocha.run();
</script>

fixtures.js,

var path = svg.selectAll("path")
          .data(pie(data))
          .enter().append("path").attr("class", "path")
          .style("fill", function(d, i) { return color(i); })
          .attr("d", arc)
          .on("mouseover", function() { d3.select(this).style("fill", "#ff0000"); })
          .on("mouseout" , function() { d3.selectAll("path").style("fill", function(d, i) { return color(i); }); });

//  I wanted to test my understanding of d3 selectors
var path_one = d3.select('.path').attr("id", "path_one"); 

tests.js,

describe('Donut Chart', function() {
    describe("Mouseover events", function() {
        it("should call on('mouseover')", function () {
            var spy = sinon.spy(path, "on");
            $('path').mouseenter();
            sinon.assert.called(spy);
        });
    });
});
Share Improve this question asked Jun 5, 2014 at 10:28 tpgmartintpgmartin 3451 gold badge4 silver badges13 bronze badges 4
  • Do you have some <path> elements in your DOM? What do you get if you call $('path') in the JS console? – Pavling Commented Jun 5, 2014 at 11:01
  • Also, what happens if you change the call to be $('path').mouseover();? (since your test is talking about mouseover rather than mouseenter) – Pavling Commented Jun 5, 2014 at 11:04
  • The path elements refer to the set of all five of the arc segments of my chart (this is d3 syntax). I was using $('path').mouseenter() as I know that gives me the correct result using pure jQuery i.e. it triggers the event as expected. Doing either $('path').mouseover() or $('path').mouseenter() returns me an array of all five path elements. – tpgmartin Commented Jun 5, 2014 at 11:17
  • sorry... I meant, in the test, change it to $('path').mouseover(); – Pavling Commented Jun 5, 2014 at 11:25
Add a ment  | 

1 Answer 1

Reset to default 8

There's a few issues here; we can sort out your syntax, but we also have to sort out the intention.

The error message "Error: expected on to have been called at least once but was never called" is accurate. on isn't called during your test. It's only called in your fixtures, to set up the event handlers. You're also triggering a mouseenter event but your listeners are for mouseover and mouseout. In the real world, you'd get a mouseover very soon after a mouseenter event, but when you fake it with jQuery that won't happen. jQuery is a non-starter anyway; see below.


You could try to fix this by changing them from anonymous functions to something named, like this:

var mouseOverHandler = function() { 
    d3.select(this).style('fill', '#ff0000');
};

And then bind it to your path with path.on('mouseover', mouseOverHandler). You'd think you can now spy on mouseOverHandler, but that won't work either. Your function will be bound when you call on, so swapping it out for a Sinon spy later won't have any effect.

jQuery triggering won't work with D3

Another problem is you can't use jQuery to trigger D3 events because jQuery events aren't DOM events. So you should replace your call to $('path').mouseenter() to something like document.getElementById('path_one').dispatchEvent(new MouseEvent('mouseover')); (Noting that this is changing the code from "trigger mouseenter on all paths" to "trigger mouseover on the element with id path_one").

Testing the wrong thing

You could futz around with refactoring so you can swap out your callback with something you can spy, but fundamentally you're testing the wrong thing. Essentially, you're trying to write a test for D3; "When I add an event listener, make sure that event listener gets called." Instead, you should test your actual code's behaviour: "When someone mouses over the chart, its colour should change".

If you really want to assert that your event handler was bound, you could do that:

expect(path.on('mouseover')).to.be.a('function')

But if you want to make sure your colour is changed, you want your test to be as follows:

describe("Mouseover events", function() {
    it("should update the colours when a user mouses over the paths", function () {
        var oldColor = path.style('fill');
        document.getElementById('path_one').dispatchEvent(new MouseEvent('mouseover'));
        expect(path.style('fill')).to.not.equal(oldColor);
    });
});
发布评论

评论列表(0)

  1. 暂无评论