DHlavaty
DHlavaty

Reputation: 13088

How to test if function was called with defined parameters ( toHaveBeenCalledWith ) with Jest

I want to test, if particular function was called in my test and with the correct parameters. From JEST documentation I'm not able to figure out, what is the correct way to do it.

Let's say I have something like this:

// add.js

function child(ch) {
   const t = ch + 1;
   // no return value here. Function has some other "side effect"
}


function main(a) {
  if (a == 2) {
    child(a + 2);
  }

  return a + 1;
}

exports.main = main;
exports.child = child;

Now in unit test:

1. I want to run main(1) and test that it returned 2 and child() was not called.

2. And then I want to run main(2) and thest that it returned 3 and child(4) was called exactly once.

I have something like this now:

// add-spec.js
module = require('./add');

describe('main', () => {

  it('should add one and not call child Fn', () => {
    expect(module.main(1)).toBe(2);
    // TODO: child() was not called
  });

  it('should add one andcall child Fn', () => {

    expect(module.main(2)).toBe(3);
    // TODO: child() was called with param 4 exactly once
    // expect(module.child).toHaveBeenCalledWith(4);
  });

});

I'm testing this in https://repl.it/languages/jest , so a working example in this REPL will be much appreciated.

Upvotes: 37

Views: 91149

Answers (4)

Artha Ali
Artha Ali

Reputation: 81

let child = require('./child');
let main = require('./add').main;

// name of the module, name of the function
spy = jest.spyOn(child, 'child');

describe('main', () => {

  it('should add one and call child Fn', () => {
    expect(main(1)).toBe(2);
    // Called or not
    expect(spy).toHaveBeenCalled();
  });



});

Upvotes: 8

Steve
Steve

Reputation: 1309

To be mocked:

// child.js
function child(ch) {
  console.log('some side effects happen in here', ch);
}

exports.child = child;

To be tested:

// main.js
const { child } = require('./child');

function main(a) {
  if (a == 2) {
    child(a + 2);
  }

  return a + 1;
}

exports.main = main;

Test for main.js

// main.test.js
jest.mock('./child');
const { main } = require('./main');

// This is the mocked version of "child"
const { child } = require('./child');

describe('main', () => {

  it('should add one and not call child Fn', () => {
    expect(main(1)).toBe(2);

    expect(child).toHaveBeenCalledTimes(0);
  });

  it('should add one and call child Fn', () => {
    expect(main(2)).toBe(3);

    expect(child).toHaveBeenCalledWith(4);
    expect(child).toHaveBeenCalledTimes(1);
  });
});

Upvotes: 0

Adrian Cubillos
Adrian Cubillos

Reputation: 23

In my case I had a similar doubt with angular code so I have a method, that is invoked when a field in a form is changed, and the only task of this method is to trigger some other methods.

Code extract:

handleConnectionToLegChange(value) {
if (!isNullOrUndefined(value)) {
  this.connectionsForm.controls.to.markAsDirty();
  this.connectionsForm.controls.to.updateValueAndValidity();
  this.connectionsForm.controls.from.markAsDirty();
  this.connectionsForm.controls.from.updateValueAndValidity();
  this.updateModalButtonStatus(this.connectionsSubject);
}}

So in order to test it I used this test case. (I just spied on 2 of the 5 triggered methods but that's enough on my case.)

test extract:

 it('should execute fn handleConnectionToLegChange and check method calls if value is not null', () => {
component.connectionsForm.controls.to.updateValueAndValidity = jest.fn();
component.updateModalButtonStatus = jest.fn();
component.handleConnectionToLegChange('a');
expect(component.connectionsForm.controls.to.updateValueAndValidity).toHaveBeenCalled();
expect(component.updateModalButtonStatus).toHaveBeenCalled(); });

It worked fine for me.

Upvotes: 0

DHlavaty
DHlavaty

Reputation: 13088

OK, I've figured it out. The trick is, to split functions into separate files. So the code is (and works in https://repl.it/languages/jest ):

// add.js
child = require('./child').child;

function main(a) {
  if (a == 2) {
    child(a + 2);
  }

  return a + 1;
}

exports.main = main;

extracted child.js file:

// child.js

function child(ch) {
   const t = ch + 1;
   // no return value here. Function has some other "side effect"
}

exports.child = child;

main test file:

// add-spec.js
main = require('./add').main;
child = require('./child').child;

child = jest.fn();

describe('main', () => {

  it('should add one and not call child Fn', () => {
    expect(main(1)).toBe(2);

    expect(child).toHaveBeenCalledTimes(0);
  });

  it('should add one andcall child Fn', () => {
    expect(main(2)).toBe(3);

    expect(child).toHaveBeenCalledWith(4);
    expect(child).toHaveBeenCalledTimes(1);
  });

});

Upvotes: 22

Related Questions