76

I have reviewed Jasmine's documentation of the toHaveBeenCalledWith matcher in order to understand whether it's possible to pass in a regular expression for an argument, if that argument is expected to be a string. Unfortunately, this is unsupported functionality. There's also an issue open on github requesting this functionality.

I've dug a bit into the codebase, and I see how it might be possible to implement this inside the existing matcher. I think it would be more appropriate to implement it as a separate matcher though, so that the abstraction is captured individually.

In the meantime, what might be a good workaround?

zealoushacker
  • 6,766
  • 5
  • 35
  • 44

6 Answers6

95

After doing some digging, I've discovered that Jasmine spy objects have a calls property, which in turn has a mostRecent() function. This function also has a child property args, which returns an array of call arguments.

Thus, one may use the following sequence to perform a regexp match on call arguments, when one wants to check that the string arguments match a specific regular expression:

var mySpy = jasmine.createSpy('foo');
mySpy("bar", "baz");
expect(mySpy.calls.mostRecent().args[0]).toMatch(/bar/);
expect(mySpy.calls.mostRecent().args[1]).toMatch(/baz/);

Pretty straightforward.

zealoushacker
  • 6,766
  • 5
  • 35
  • 44
42

As of Jasmine 2.2, you can use jasmine.stringMatching:

var mySpy = jasmine.createSpy('foo');
mySpy('bar', 'baz');
expect(mySpy).toHaveBeenCalledWith(
  jasmine.stringMatching(/bar/),
  jasmine.stringMatching(/baz/)
);
Rusty Shackleford
  • 1,111
  • 2
  • 14
  • 19
  • 2
    This should be the accepted solution if you are using the most recent Jasmine. I received errors when I tried using `.argsForCall` and `.args` – dzylich Apr 09 '18 at 15:16
  • 7
    And this is why my default answer view is "Active" and not "Votes." – Paul Oliver Sep 23 '18 at 00:30
24

In Jasmine 2.0 the signature changed a bit. Here it would be:

var mySpy = jasmine.createSpy('foo');
mySpy("bar", "baz");
expect(mySpy.calls.mostRecent().args[0]).toMatch(/bar/);
expect(mySpy.calls.mostRecent().args[1]).toMatch(/baz/);

And the Documentation for Jasmine 1.3 has moved.

jammon
  • 3,404
  • 3
  • 20
  • 29
13

Alternatively, if you are spying on a method on an object:

spyOn(obj, 'method');
obj.method('bar', 'baz');
expect(obj.method.argsForCall[0][0]).toMatch(/bar/);
expect(obj.method.argsForCall[0][1]).toMatch(/baz/);
Nick Hingston
  • 8,724
  • 3
  • 50
  • 59
  • 5
    In Jasmine 2.0, the syntax would be `obj.method.calls.argsFor(index)`, still an Array. – Dr1Ku Sep 18 '15 at 11:13
  • If you're trying this in angular (2+), typescript doesn't seem to like it so you might have to do use bracket notation `obj.method['calls']['argsFor'](index)` to get around type checking – Crhistian Ramirez Aug 08 '18 at 17:17
8

Sometimes it is more readable to write it this way:

spyOn(obj, 'method').and.callFake(function(arg1, arg2) {
    expect(arg1).toMatch(/bar/);
    expect(arg2).toMatch(/baz/);
});
obj.method('bar', 'baz');
expect(obj.method).toHaveBeenCalled();

It give more clear visibility of method arguments (instead of using array)

Maciej Dzikowicki
  • 859
  • 1
  • 10
  • 10
7

As jammon mentioned, the Jasmine 2.0 signature has changed. If you are spying on the method of an object in Jasmine 2.0, Nick's solution should be changed to use something like -

spyOn(obj, 'method');
obj.method('bar', 'baz');
expect(obj.method.calls.mostRecent().args[0]).toMatch(/bar/);
expect(obj.method.calls.mostRecent().args[1]).toMatch(/baz/);
Blake
  • 2,357
  • 3
  • 25
  • 35