PDA

View Full Version : Jasmine Unit Testing a MVC controller



fifeflyer69
2 Apr 2012, 5:10 AM
I am trying to unit test a MVC controller using Jasmine and having trouble getting the controller to detect a fireEvent.

The test spec:



it("has invoked the login callback", function() {
spyOn(this.controller, 'onLogin').andReturn(true);
var login_btn = this.controller.getLoginform().down('#LoginButton');
login_btn.fireEvent('click');

expect(this.controller.onLogin).toHaveBeenCalled();
});


The controller has the following listener setup:



init: function(application_instance) {
// Handle events fired from views controlled by this controller.
this.control({
'#LoginButton': {
click: this.onLogin
}
});
},



Any help iin getting this working would be appreciated.

fifeflyer69
2 Apr 2012, 5:52 AM
I have figured out that the event is being detected, but Jasmine is not detecting that onLogin is being called! If I create a spy for onLogin, Jasmine still does not detect it!



it("has invoked the login callback", function() {
//spyOn(this.controller, 'onLogin').andReturn(true);
this.controller.onLogin = jasmine.createSpy("Login Spy").andCallFake(function() {
console.log("Fake onLogin called");
});

var login_btn = this.controller.getLoginform().down('#LoginButton');
console.log("About to fire event");
login_btn.fireEvent('click');
console.log("Fired event");

expect(this.controller.onLogin).toHaveBeenCalled();
});


Any Jasmine experts out there that can provide guidance?

börn
2 Apr 2012, 11:03 PM
How do you know the fireEvent worked? Did you see some logs on the console? I always have some trouble with views and components which wasn't really there at the time i wanted to access them in a jasmine test...

fifeflyer69
5 Apr 2012, 3:49 AM
Hi Born,

Adding a breakpoint to onLogin in Firebug proves that the event is being fired.

If I set a variable (called) to true when onLogin is invoked and replace the expect in my test to the following, I can get the test to pass!

expect(this.controller.called).toBeTruthy();

So I'm puzzled as to why the following expect always fails :-?

expect(this.controller.onLogin).toHaveBeenCalled();

jtravis
18 Apr 2012, 7:28 AM
Just based off of what I've seen out there in comments about this issue, and from my own limited experience, this probably has to do with a conflict between how Ext and Jasmine work.

When you set up a spy, Jasmine is probably replacing the existing pointer to the method you are spying on, thereby inserting itself into the process and giving it the ability to report on when the method has been called. Inside your controller's init method, the control() function gets called, which sets up the criteria for which events and components your handlers will be attached to.

Inside the control() call, a pointer to the appropriate handler method is passed. If Jasmine works the way I think it does, then this is the key to the problem we are experiencing. Your application already has a pointer to your handler, and will continue to use it rather than the replaced pointer that Jasmine has created.

This hypothesis is backed up by the results that others have seen, where setting up your Jasmine spies before calling the init() method on the controller allows the spy to properly track your handlers. My own research has indicated that setting up the spies inside of the init() method, but before the call to the control() method also works.

So my mechanism for getting spies to work is setting a flag inside of my application:

Ext.onReady(function() {
Application = Ext.create('Ext.app.Application', {
name: 'ADC',
testing: true,

And then the controllers can set up the spies in their init() methods based off of this flag:

init: function() {
var me = this;

if(me.application.testing) {
spyOn(me, 'addDiscount').andCallThrough();
}

me.control({
'#AddDiscountButton': {
click: me.addDiscount
}


Probably not the most elegant of solutions, but it does allow the toHaveBeenCalled() Matcher to pass successfully.

Vital Aaron
8 May 2012, 1:23 PM
Just to save readers a little time... and example of the "setting up your Jasmine spies before calling the init() method" technique can be found at http://www.sencha.com/forum/showthread.php?130522-B3-MVC-Controller-Unit-Test-Problems/page2

code_expert
31 Jul 2012, 2:29 PM
Hi,

I have the exact same problem, and I have the spy before calling the init of the controller. Can you please explain more how you solved the issue?

Thanks

recacha
16 May 2013, 10:17 PM
I had this problem and I solved it (thanks to @juanghurtado (http://twitter.com/juanghurtado), great!!).

What is the problem? We can NOT spy a method which is linked to event. That is, we can't spy onLogin method here:

click: this.onLogin

If you want to spy this method, you must call it indirectly, as follows:


click: function() {
this.onLogin.apply(this, arguments)
}

I hope that it will help. Regards!