PDA

View Full Version : spyOn a controller with jasmine



keripix
5 Dec 2011, 12:07 AM
var ctrl = Application.getController("document.DocumentUpload"),
pathField = Ext.ComponentQuery.query("#pathField")[0];

expect(ctrl).toBeDefined(); //first test

spyOn(ctrl,'validateFile');

pathField.fireEvent("change"); // second test

expect(ctrl.validateFile).toHaveBeenCalled();


The above code is a simplified version of the real one.

The first test passed, but the second one failed. I then added:

console.log("validate has been called");
inside the validateFile method, then checked while running the test. The above log was called. So its correct if i assume the method was actually called, right?

If that's the case, then why jasmine failed?

Well, I then tried to modify the test and validateFile() method. I added a new property to my controller:

validateFileHasBeenCalled : false

then inside validateFile i set the above property to true, so that when i run the test, I can check wether validateFile has been called. Inside validateFIle():



this.validateFileHasBeenCalled = true;


The test passed.

So, im still very curious, why did jasmine failed on my first try? Did i do something wrong? Or have i misunderstood something?

PS: im still new to javascript, extjs and tdd.

Thanks in advance

arthurakay
5 Dec 2011, 7:20 AM
I've pretty familiar with Jasmine, but this is a bit tricky to debug.

If I had to guess, your second test (in the original code example) might be called before your event has finished firing.

In other words, the "fireEvent" call launches an asynchronous series of method calls. Because it's asynchronous, your unit test suite continues past the "fireEvent" method and runs the second test.

You could delay the running of that second test by calling a waits() method (from Jasmine). See this in their API:
- https://github.com/pivotal/jasmine/wiki/Asynchronous-specs

Hopefully that helps.

keripix
5 Dec 2011, 7:54 PM
Ok, I've read the link, and then modified my controller to:


....
validateFile: function(....){
this.validateFileCalled = true;
},

validateFileCalled : false,

validateFileHasBeenCalled: function(){
return this.validateFileCalled;
}



The modified test code:



spyOn(ctrl,"validateFile").andCallThrough();

pathField.fireEvent("change");

waitsFor(function(){
return ctrl.validateFileHasBeenCalled();
},"Too Long",5000);

expect(ctrl.validateFileCalled).toEqual(true);
expect(ctrl.validateFile).toHaveBeenCalled();


the test then failed on expecting the validateFile method has been called.

keripix
5 Dec 2011, 8:52 PM
I forgot to mention, that the controller above has this for init method:



init: function() {
this.control({
....


'#pathField': {


change: this.validateFile


}


});

}

arthurakay
6 Dec 2011, 6:04 AM
So we're starting to get into territory that isn't best suited for the Sencha forums (i.e. Jasmine support), but here's my best shot.

Take a look at the docs for the Jasmine spy:
- http://pivotal.github.com/jasmine/jsdoc/symbols/jasmine.Spy.html

Using this info, I added the following to my own tests:



//jasmine.createSpy('foobar'); //I don't know if I need this or not...
spyOn(ctrl, 'validateFile').andCallThrough();

...

expect(ctrl.validateFile).toHaveBeenCalled();

I believe the "andCallThrough" method is the key. My own tests succeed using this, but fail if I don't have the "andCallThrough" method.

If this doesn't work, I suggest trying the help groups for Jasmine. Your approach to testing ExtJS seems fine to me, so the issue has nothing to do with Sencha - it's an implementation detail of Jasmine.

Let me know how it goes. ~o)