I would like to mock a function with Jest, but only if it is called with specific arguments, for example:
function sum(x, y) {
return x + y;
}
// mock sum(1, 1) to return 4
sum(1, 1) // returns 4 (mocked)
sum(1, 2) // returns 3 (not mocked)
There is a similar feature implemented in the Ruby's RSpec library:
class Math
def self.sum(x, y)
return x + y
end
end
allow(Math).to receive(:sum).with(1, 1).and_return(4)
Math.sum(1, 1) # returns 4 (mocked)
Math.sum(1, 2) # returns 3 (not mocked)
What I'm trying to achieve in my tests is a better decoupling, let's say I want to test a function that relies on sum
:
function sum2(x) {
return sum(x, 2);
}
// I don't want to depend on the sum implementation in my tests,
// so I would like to mock sum(1, 2) to be "anything I want",
// and so be able to test:
expect(sum2(1)).toBe("anything I want");
// If this test passes, I've the guarantee that sum2(x) is returning
// sum(x, 2), but I don't have to know what sum(x, 2) should return
I know that there is a way to implement this by doing something like:
sum = jest.fn(function (x, y) {
if (x === 1 && y === 2) {
return "anything I want";
} else {
return sum(x, y);
}
});
expect(sum2(1)).toBe("anything I want");
But it would be nice if we had some sugar function to simplify it.
Does it sounds reasonable? Do we already have this feature in Jest?
Thanks for your feedback.
I found this library that a colleague of mine wrote recently: jest-when
import { when } from 'jest-when';
const fn = jest.fn();
when(fn).calledWith(1).mockReturnValue('yay!');
const result = fn(1);
expect(result).toEqual('yay!');
Here's the library: https://github.com/timkindberg/jest-when
No there is no way to do this in Jest yet. You could use sinons stubs for this. from the docs:
stub.withArgs(arg1[, arg2, ...]);
Stubs the method only for the
provided arguments. This is useful to be more expressive in your
assertions, where you can access the spy with the same call. It is
also useful to create a stub that can act differently in response to
different arguments.
"test should stub method differently based on arguments": function () {
var callback = sinon.stub();
callback.withArgs(42).returns(1);
callback.withArgs(1).throws("TypeError");
callback(); // No return value, no exception
callback(42); // Returns 1
callback(1); // Throws TypeError
}
This may help...
I had something similar whereby I had the same method called with different parameters requiring different returned result from the stubbed/mocked call. I've used a variable with a list of functions when I've made a call to the mocked service i take function off the top of the queue and execute function. It requires knowledge of the order of execution you are testing and doesn't really handle varying the response by argument but has allowed me to work around the restriction in jest.
var mockedQueue = [];
mockedQueue.push(() => {return 'A';})
mockedQueue.push(() => {return 'B';})
service.invoke = jest.fn(()=>{
serviceFunctionToCall = mockedQueue.shift();
return serviceFunctionToCall();
})
jest-when
mentioned by STeve Shary
is probably the best option.
If you don't want to install a new library, when you don't care about the original function, here's a one-line solution:
sum.mockImplementation((x, y) => x === 1 && y === 2 && "my-return-value")