问题
Trying to test that an event handler gets called on a clicked element with Jasmine. Have a "Pad" object that contains a DOM element "PadElement", which gets clicked. The event handler is a method on the Pad object:
GRAPH.Pad = function(graphDiv, graph) {
this.graph = graph;
this.clickHandler = function(e) {
console.log('padElement clickHandler called');
//this.graph.createVertex(e.clientX, e.clientY);
};
this.padElement = GRAPH.padElement(graphDiv, this.clickHandler);
}
GRAPH.padElement = function(graphDiv, clickHandler) {
//Initialize pad
var NS="http://www.w3.org/2000/svg";
var pad=document.createElementNS(NS,"svg");
pad.setAttributeNS(null, 'id', 'pad');
graphDiv.appendChild(pad);
pad.addEventListener('click', clickHandler)
return pad;
}
The Jasmine test:
var testDiv = document.createElement('div');
var testGraph = new GRAPH.Graph(testDiv);
var testPad = new GRAPH.Pad(testDiv, testGraph);
it('has its clickHandler function called when its padElement is clicked',
function() {
spyOn(testPad, "clickHandler");
simulateClick(testPad.padElement);
//testPad.clickHandler();
expect(testPad.clickHandler).toHaveBeenCalled();
});
However, the test FAILS. Note that the event listener does get called (console.log writes successfully with a mouse click and with simulateClick), AND if I just call the testPad.clickHandler() directly Jasmine's spy can pick it up. But what happens during the actual test? Does the event handler invocation get transferred to a different object at runtime? What's the right way to do this?
回答1:
You are actually testing that GRAPH.padElement
calls the supplied clickHandler
and not that this.clickHandler
of GRAPH.Pad
is called by GRAPH.padElement
. How I would do that is
var testDiv = document.createElement('div');
var clickHandlerSpy = jasmine.CreateSpy();
var padelement = padElement(testDiv , clickHandlerSpy);
it('has its clickHandler function called when its padElement is clicked',
function() {
simulateClick(testPad.padElement);
expect(clickHandlerSpy).toHaveBeenCalled();
});
This may sound little different to what you are trying to achieve. But in ideal unit testing world you should be testing each unit independently, so I would first test that padElement
does what it's supposed to do (as above) and then write another test to make sure GRAPH.Pad
is passing correct handler to padElement
. Now to do that I would not create padElement
directly from within GRAPH.Pad
but somehow inject it from outside and then mock it in the jasmine specs. If you are not clear on this part let me know and I can put some code together for you.
回答2:
To add a more generic explanation:
You need to spy before attaching the function as an event listener.
Some pseudo-code:
it("succeeds", function(){
var o = {}
o.f = function() { console.log("event caught!");} //your eventHandler
spyOn(o, "f").and.callThrough(); //o.f is now a spy
addEventListener('click', o.f); //spy listens for event
fireEvent("click"); //"event caught!" is written to console
expect(o.f).toHaveBeenCalled(); //success, we're happy
});
it("fails", function(){
var o = {}
o.f = function() { console.log("event caught!");} //your eventHandler
addEventListener('click', o.f); //your eventHandler function listens for event
spyOn(o, "f").and.callThrough(); //o.f is now a spy
fireEvent("click"); //"event caught!" is written to console
expect(o.f).toHaveBeenCalled(); //fail, we waste a couple of hours not understanding why
});
So if you know your handler is being called, but the spy does not register as .tohaveBeenCalled()
check what happens first: your spy or the assignment of the listener. The spy needs to come first.
来源:https://stackoverflow.com/questions/10378189/jasmine-cant-spy-on-event-handler