How to reset a spy or mock in jest - reactjs

I have a function that I have mocked in my test cases file.
MyService.getConfigsForEntity = jest.fn().mockReturnValue(
new Promise(resolve => {
let response = [];
resolve(response);
})
);
Now I want all my test cases in that file to use this mock like
describe('Should render Component Page', () => {
it('should call the API', async () => {
const {container} = render(<MyComp entityName='entity1'/>);
await wait(() => expect(MyService.getConfigsForEntity).toHaveBeenCalled());
});
});
The only issue is in only one of the test case I want to mock the return value differently.
But all other test cases before and after can use the global mock.
describe('Should call API on link click', () => {
it('should call the API on link click', async () => {
const spy = jest.spyOn(MyService, 'getConfigsForEntity ').mockReturnValue(
new Promise(resolve => {
let response = [{
"itemName": "Dummy"
}];
resolve(response);
});
const {container} = render(<MyComp entityName='entity1'/>);
await wait(() => expect(spy).toHaveBeenCalled());
spy.mockClear();
});
});
The problem is , once I mock the function differently inside one test case , all other test cases after that test case, that are using the global mock , are failing,
But it only works if I put the test case after all other test cases.
What am I doing wrong?

You can try with mockRestore():
beforeEach(() => {
spy.mockRestore();
});

have you tried?
beforeEach(() => {
jest.clearAllMocks();
})

Related

Enzyme mocked axios PUT not being called

I have a component which has a form with a submit button which when clicked performs an axios.put which I want to intercept and test.
So far I have the following code simplified for this example:
describe('Edit Client functionality', () => {
beforeEach(() => {
const mock = new MockAdapter(axios);
mock
.onPut('http://localhost:5000/api/entity/client/1')
.reply(200, { success: true });
});
it('Dummy example test', done => {
const component = mount(<DummyComponent />);
const spy = jest.spyOn(axios, 'put');
component.find('form').simulate('submit')
setTimeout(() => {
expect(spy).toHaveBeenCalled();
done();
}, 0);
})
I've tried many variations of the above but can't get the mocked PUT to be called. Any suggestions appreciated.

Jest simple async await with timer not working

I'm trying to get a simple async/await test working with a setTimeout but nothing is happening when I run it:
const testing = async () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('result');
}, 500);
});
}
jest.useFakeTimers()
it('tests async await', async () => {
const r = await testing();
expect(r).toBe('result');
jest.runAllTimers();
});
I'd be fine with using real setTimeout like in Jasmine but it seems in Jest you have to use fake ones. So I did include jest.useFakeTimers() and jest.runAllTimers() but that didn't solve it.
The test gets stuck and never completes. Any idea what could be the issue?
Try following:
it('tests async await', async () => {
jest.useFakeTimers();
testing = async () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('result');
}, 500);
});
};
const asyncResult = testing();
jest.runAllTimers();
const r = await asyncResult;
expect(r).toBe('result');
});

How do I properly test for a rejected promise using Jest?

Code
import { createUser } from '../services';
...
...
handleFormSubmit = () => {
this.setState({ loading: true });
createUser()
.then(() => {
this.setState({
loading: false,
});
})
.catch(e => {
this.setState({
error: e,
});
});
};
Test
it('rejects...', () => {
const Container = createUserContainer(CreateUser);
const wrapper = shallow(<Container />);
return wrapper.instance().handleFormSubmit()
.catch(e => {
console.log("State: ", wrapper.state());
expect(e).toEqual('error');
});
});
Mock
export const createUser = function() {
return new Promise((resolve, reject) => {
reject('error');
});
};
The test does force the code to go into the catch in the method. So the state does get set to 'error'.
But in my test, it doesn't do what I expect and wait for the Promise to reject before it tests for the state change.
I'm not sure what to try here, should I be using async/await?
So it's the createUser method I want to wait for but I'm not sure my implementation allows for this.
You should do something like this:
it('rejects...', () => {
const Container = createUserContainer(CreateUser);
const wrapper = shallow(<Container />);
return expect(wrapper.instance().handleFormSubmit()).rejects.toEqual('error');
});
I think it is cleaner this way. You can see this approach in the official docs.
It's important to note that .rejects (and .resolves) returns a promise, which is returned in the example above so that jest knows to wait on it. If you don't return it, you MUST await it:
it('rejects...', async () => {
const Container = createUserContainer(CreateUser);
const wrapper = shallow(<Container />);
await expect(wrapper.instance().handleFormSubmit()).rejects.toEqual('error');
});
The test fails because it's not aware that the subject is asynchronous. It can be fixed by using a done param or making the test function async.
Note it's also necessary to set the number of expected assertions so that the test will fail even if the catch branch is not taken.
async/await style:
it('rejects...', async () => {
expect.assertions(1);
const Container = createUserContainer(CreateUser);
const wrapper = shallow(<Container />);
await wrapper.instance().handleFormSubmit()
.catch(e => {
console.log("State: ", wrapper.state());
expect(e).toEqual('error');
});
});
Older style done param:
it('rejects...', done => {
expect.assertions(1);
const Container = createUserContainer(CreateUser);
const wrapper = shallow(<Container />);
wrapper.instance().handleFormSubmit()
.catch(e => {
console.log("State: ", wrapper.state());
expect(e).toEqual('error');
done();
});
});
Asynchronous Testing Reference
expect.assertions reference
Your code looks correct. Why do you say that it doesn't wait for the Promise to reject? The only difference I would make would be to make use of Jest's mocking capability, so change
Mock
export const createUser = function() {
return new Promise((resolve, reject) => {
reject('error');
});
};
to
Test
jest.mock('../services');
const services = require('../services');
const createUser = jest.spyOn(services, "createUser");
createUser.mockRejectedValue("error");
...
it('rejects...', () => {
There's no need to have a separate Mock file
In your code handleFormSubmit function should return Promise on which you can wait in your test. Also you need to return truthful data from success and error callback to resolve and reject the promise respectively.
handleFormSubmit = () => {
this.setState({ loading: true });
return createUser()
.then(() => {
this.setState({
loading: false,
});
return true;
})
.catch(e => {
this.setState({
error: e,
});
throw e;
});
};
Here in your actual code you have caught the error in catch handler and trying to catch it further in out test case code. Hence catch can not be chained further, while you can chain then multiple times.
For reference go through Promise documentations:
https://www.peterbe.com/plog/chainable-catches-in-a-promise

How to unit test Promise catch() method behavior with async/await in Jest?

Say I have this simple React component:
class Greeting extends React.Component {
constructor() {
fetch("https://api.domain.com/getName")
.then((response) => {
return response.text();
})
.then((name) => {
this.setState({
name: name
});
})
.catch(() => {
this.setState({
name: "<unknown>"
});
});
}
render() {
return <h1>Hello, {this.state.name}</h1>;
}
}
Given the answers below and bit more of research on the subject, I've come up with this final solution to test the resolve() case:
test.only("greeting name is 'John Doe'", async () => {
const fetchPromise = Promise.resolve({
text: () => Promise.resolve("John Doe")
});
global.fetch = () => fetchPromise;
const app = await shallow(<Application />);
expect(app.state("name")).toEqual("John Doe");
});
Which is working fine. My problem is now testing the catch() case. The following didn't work as I expected it to work:
test.only("greeting name is 'John Doe'", async () => {
const fetchPromise = Promise.reject(undefined);
global.fetch = () => fetchPromise;
const app = await shallow(<Application />);
expect(app.state("name")).toEqual("<unknown>");
});
The assertion fails, name is empty:
expect(received).toEqual(expected)
Expected value to equal:
"<unknown>"
Received:
""
at tests/components/Application.spec.tsx:51:53
at process._tickCallback (internal/process/next_tick.js:103:7)
What am I missing?
The line
const app = await shallow(<Application />);
is not correct in both tests. This would imply that shallow is returning a promise, which it does not. Thus, you are not really waiting for the promise chain in your constructor to resolve as you desire. First, move the fetch request to componentDidMount, where the React docs recommend triggering network requests, like so:
import React from 'react'
class Greeting extends React.Component {
constructor() {
super()
this.state = {
name: '',
}
}
componentDidMount() {
return fetch('https://api.domain.com/getName')
.then((response) => {
return response.text()
})
.then((name) => {
this.setState({
name,
})
})
.catch(() => {
this.setState({
name: '<unknown>',
})
})
}
render() {
return <h1>Hello, {this.state.name}</h1>
}
}
export default Greeting
Now we can test it by calling componentDidMount directly. Since ComponentDidMount is returning the promise, await will wait for the promise chain to resolve.
import Greeting from '../greeting'
import React from 'react'
import { shallow } from 'enzyme'
test("greeting name is 'John Doe'", async () => {
const fetchPromise = Promise.resolve({
text: () => Promise.resolve('John Doe'),
})
global.fetch = () => fetchPromise
const app = shallow(<Greeting />)
await app.instance().componentDidMount()
expect(app.state('name')).toEqual('John Doe')
})
test("greeting name is '<unknown>'", async () => {
const fetchPromise = Promise.reject(undefined)
global.fetch = () => fetchPromise
const app = shallow(<Greeting />)
await app.instance().componentDidMount()
expect(app.state('name')).toEqual('<unknown>')
})
By the looks of this snippet
.then((response) => {
return response.text();
})
.then((name) => {
this.setState({
name: name
});
})
it seems that text would return a string, which then would appear as the name argument on the next 'then' block. Or does it return a promise itself?
Have you looked into jest's spyOn feature? That would help you to mock not only the fetch part but also assert that the setState method was called the correct amount of times and with the expected values.
Finally, I think React discourages making side effects inside constructor. The constructor should be used to set initial state and other variables perhaps. componentWillMount should be the way to go :)
Recently, I have faced the same issue and ended up resolving it by following way
(taking your code as an example)
test.only("greeting name is 'John Doe'", async () => {
const fetchPromise = Promise.resolve(undefined);
jest.spyOn(global, 'fetch').mockRejectedValueOnce(fetchPromise)
const app = await shallow(<Application />);
await fetchPromise;
expect(app.state("name")).toEqual("<unknown>");});
Another way if you don't want to call done then return the next promise state to jest. Based on result of assertion( expect ) test case will fail or pass.
e.g
describe("Greeting", () => {
test("greeting name is unknown", () => {
global.fetch = () => {
return new Promise((resolve, reject) => {
process.nextTick(() => reject());
});
};
let app = shallow(<Application />);
return global.fetch.catch(() => {
console.log(app.state());
expect(app.state('name')).toBe('<unknown>');
})
});
});

spyOn fail even if the spy was called

In my component I have ...
onSubmit = (e) => {
e.preventDefault();
const { history, versionStore } = this.props;
versionStore.add(this.state.formData)
.then(() => history.push('/'));
}
On my test...
it('after successfully submit should redirect to / page', () => {
const spy = jest.spyOn(minProps.history, 'push')
.mockImplementation((path) => {
console.log('called with ', path); // IS CALLED!
});
const wrapper = shallow(<Add.wrappedComponent {...minProps} />);
fetchMock.postOnce('/api/version', { name: 'v1' });
wrapper.setState({ formData: { name: 'v1' } });
wrapper.find('form').simulate('submit', { preventDefault: jest.fn() });
expect(spy).toHaveBeenCalledWith('/');
spy.mockReset();
spy.mockRestore();
});
The test fail with
called with /
expect(jest.fn()).toHaveBeenCalledWith(expected)
Expected mock function to have been called with: ["/"]
But it was not called.
your redirect is inside of asynchronous code and you are testing it in a synchronous manner, meaning when the test executes the promise is not resolved yet. I would tackle this in one of 2 ways
1 - test your submit function w/o the event, then you can return the promise and test the redirection after the promise chain is successful
2 - mock versionStore.add to be synchronous and immidattly execute it's then function.

Resources