79

I've just started using Jasmine so please forgive the newbie question but is it possible to test for object types when using toHaveBeenCalledWith?

expect(object.method).toHaveBeenCalledWith(instanceof String);

I know I could this but it's checking the return value rather than the argument.

expect(k instanceof namespace.Klass).toBeTruthy();
screenm0nkey
  • 18,405
  • 17
  • 57
  • 75

2 Answers2

124

I've discovered an even cooler mechanism, using jasmine.any(), as I find taking the arguments apart by hand to be sub-optimal for legibility.

In CoffeeScript:

obj = {}
obj.method = (arg1, arg2) ->

describe "callback", ->

   it "should be called with 'world' as second argument", ->
     spyOn(obj, 'method')
     obj.method('hello', 'world')
     expect(obj.method).toHaveBeenCalledWith(jasmine.any(String), 'world')
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Wolfram Arnold
  • 7,159
  • 5
  • 44
  • 64
  • 25
    jasmine.any(Function) is handy, too – stefan.s Apr 12 '12 at 10:01
  • 2
    Noticed that it also works inside on references. for example: `expect(obj.method).toHaveBeenCalledWith({done: jasmine.any(Function)})`. Very useful. – fncomp Jan 21 '15 at 09:19
  • 2
    As of the time of this writing, Jasmine does not check for the same instance of `new String('world')` when using `toHaveBeenCalledWith(...arguments)`. [Click here](https://medium.com/@fagnerbrack/mocking-can-lean-to-nondeterministic-tests-4ba8aef977a0) to understand why that is important. – Fagner Brack Jun 30 '16 at 05:54
  • 1
    Had to check if a callback function was called, `jasmin.any(Function)` saved my life, thank you –  Jul 07 '17 at 09:47
58

toHaveBeenCalledWith is a method of a spy. So you can only call them on spy like described in the docs:

// your class to test
var Klass = function () {
};

Klass.prototype.method = function (arg) {
  return arg;
};


//the test
describe("spy behavior", function() {

  it('should spy on an instance method of a Klass', function() {
    // create a new instance
    var obj = new Klass();
    //spy on the method
    spyOn(obj, 'method');
    //call the method with some arguments
    obj.method('foo argument');
    //test the method was called with the arguments
    expect(obj.method).toHaveBeenCalledWith('foo argument');   
    //test that the instance of the last called argument is string 
    expect(obj.method.calls.mostRecent().args[0] instanceof String).toBeTruthy();
  });

});
Roger C S Wernersson
  • 6,362
  • 6
  • 34
  • 45
Andreas Köberle
  • 106,652
  • 57
  • 273
  • 297
  • 1
    Andreas, is there any reason you added `.toBeTruthy()`? It seems like that is unnecessary. – jds Sep 23 '13 at 15:38
  • 1
    @gwg `expect(foo)` without a matcher is a no-op; the line would do nothing without the `toBeTruthy()` call. See http://jsfiddle.net/2doafezv/2/ for proof. – Mark Amery Feb 18 '15 at 23:42
  • 8
    This is out of date; `obj.method.mostRecentCall` needs to become [`obj.method.calls.mostRecent()`](http://jasmine.github.io/2.0/introduction.html#section-Other_tracking_properties) in Jasmine 2.0. Also, using `jasmine.any()`, as described in the other answer, is clearer and cuter. Finally, this answer takes a while to get to the point; essentially everything you wrote besides `expect(obj.method.mostRecentCall.args[0] instanceof String).toBeTruthy();` isn't really needed to explain yourself. – Mark Amery Feb 18 '15 at 23:54