Jasmine Unit Tests Unexpected this Reference

649 views Asked by At

I am trying to test a fairly simple JavaScript function in Jasmine, however the first statement is throwing an error for being undefined.

myClass.prototype.functiontoBeTested = function() {
var x = this.method()
...
}

The above throws an error in Jasmine as method is not a function and is undefined. The prototype is altered earlier to have this method, and out of curiosity I assigned this method to my test object in the spec itself as such:

myObject.method = function(){return mockResults;};

So I decided to log this to the console and instead of it being my object, I see Window {top: Window, location: Location, document: document, window: Window, external: Object…} which doesn't seem right. I've had this issue before with testing a function in Jasmine that used this but just changed the source code to refer to the object by name since the function was being assigned to something within the closure. I can't do that in this case, and I'm curious why this is referring to something unexpected (at least to me).

Edit: Some details on what the test case looks like as requested:

it("updates the control count", function(){
    var mockResults = { ... };
    myObject.method = function() {return mockResults;};
    expect(myObject.method).not.toThrow();
});

Right now I'm just trying to get the method to execute to completion during the test. The function to be tested updates the text on some HTML components, I'll work on verifying those once I can get it to actually run. The method that is causing an error is the first line of the function, and is simply an accessor method for the object being called. In actual execution, var x = this.method() runs without issue. When testing in jasmine var x = this.method() throws an error because method() is undefined for this. Instead of this referring to the calling object, it is referring to the window. This doesn't happen live, but only during testing with Jasmine. This method is undefined even when I forcibly define it for the test object just prior to execution in the test as above. That's when I decided to log this to console in the source code and realized it isn't referring to what I would have expected it to refer to.

1

There are 1 answers

1
Michael Radionov On BEST ANSWER

In JavaScript this for a method depends on the context it was called from. When you do a call myObject.method(), then method was called from the context of myObject, therefore this is myObject.

When you pass your function to Jasmine toThrow matcher, it calls it as it was passed (see source code):

try {
    actual();
} catch (e) {
   // ....
}

Here, actual() is a call of your method. It has no any specific context, so by default it will be called from window context.

Solution is to explicitly bind you method to myObject like the following:

expect(myObject.method.bind(myObject)).not.toThrow();

Credits to questions, you can find more details there: