JavaScript Testing: Jasmine Spies

JavaScript Testing: Jasmine Spies

posted in javascript on  •  • last updated on

Spies, the Jasmine implementation for mocks featuring spyOn & spyOnProperty as well as jasmine.createSpy(Obj) and how to inspect calls made.

spyOn

var bar = 0;
const foo = {
  setBar(value, times) {
    bar = value * (times || 1);
    return bar;
  }
};

beforeEach(() => {
  const theSpy = spyOn(foo, 'setBar');
  expect(theSpy.and.identity()).toBe('setBar');

  foo.setBar(1);
  foo.setBar(2, 10);
});

it('can check if called', () => {
  expect(foo.setBar).toHaveBeenCalled();
  expect(foo.setBar.calls.any()).toBe(true);
});

it('how many times', () => {
  expect(foo.setBar).toHaveBeenCalledTimes(2);
  expect(foo.setBar.calls.count()).toBe(2);
});

it('and with which parameters', () => {
  expect(foo.setBar).toHaveBeenCalledWith(1);
  expect(foo.setBar).toHaveBeenCalledWith(2, 10);
});

// Also:
// toHaveBeenCalledBefore(anotherSpy)

it('but does not execute the function', () => {
  expect(bar).toBe(0);
});

Or create your own spies:

it('create bare spy without implementation', () => {
  const spy = jasmine.createSpy().and.returnValue(true);
  expect(spy()).toBe(true);
});

it('create spy object with spy functions action1 and action2', () => {
  const spyObj = jasmine.createSpyObj('id', ['action1', 'action2']);
  spyObj.action1();
  expect(spyObj).toHaveSpyInteractions();
  expect(spyObj.action1).toHaveBeenCalled();
  expect(spyObj.action2).not.toHaveBeenCalled();
});

it('create spy object with return values 15 and 18', () => {
  const spyObj = jasmine.createSpyObj('id', {action1: 15, action2: 18});
  expect(spyObj.action1()).toBe(15);
});

Spy.and

beforeEach(() => {
  this.foo = foo;
});

it('can actually execute the function', () => {
  spyOn(this.foo, 'setBar').and.callThrough();
  foo.setBar(2, 10);
  expect(bar).toBe(2 * 10);
});

it('or do something else', () => {
  spyOn(this.foo, 'setBar').and.callFake(value => value + 1);
  const calced = foo.setBar(5);
  expect(calced).toBe(6);
});

it('allows returning canned values', () => {
  spyOn(this.foo, 'setBar').and.returnValue(1);
  //.and.returnValues('first call result', 'second');
  const canned = foo.setBar();
  expect(canned).toBe(1);
});

it('or just throw an error', () => {
  spyOn(this.foo, 'setBar').and.throwError('quux');
  expect(foo.setBar).toThrowError('quux');
});

Spy.calls

it('knows about arguments, returnValues and more', () => {
  const spie = jasmine.createSpy('identity').and.callFake(v => v * 5);
  spie(1);
  spie(2, 'arg');

  // spie.calls.any() and spie.calls.count()

  // Full callData
  expect(spie.calls.first()).toBe(spie.calls.all()[0]);
  expect(spie.calls.mostRecent()).toEqual({
    object: jasmine.any(Object),
    args: [2, 'arg'],
    returnValue: 10,
    invocationOrder: jasmine.any(Number)
  });

  // Just the arguments
  expect(spie.calls.argsFor(0)).toEqual([1]);
  expect(spie.calls.allArgs()).toEqual([[1], [2, 'arg']]);

  spie.calls.reset();
  expect(spie).not.toHaveBeenCalled();
});

spyOnProperty

const foop = {
  get value() {},
  set value(v) {}
};

it('can spy on getter', () => {
  spyOnProperty(foop, 'value', 'get').and.returnValue(1);
  expect(foop.value).toBe(1);
});

it('and on setters', () => {
  const spiez = spyOnProperty(foop, 'value', 'set');
  foop.value = true;
  expect(spiez).toHaveBeenCalled();
});

Stuff that came into being during the making of this post
Updates
  • 28 March 2023 : Updated to Jasmine 4.3.0.
Tags: tutorial testing