In my react-native app I want to make a post request to my server, and depending on the result, I want to show a message. I'd like to unit-test this using jest, mocking away the call to the backend.
I tried to create a minimal example to show what's going on:
This is the Component I am testing
import React from 'react';
import { Alert } from 'react-native';
import { create } from 'apisauce';
const api = create({ baseURL: "someurl" });
export class ApisauceExample extends React.Component {
upload() {
api.post('/upload')
.then(() => { Alert.alert('Success!', 'Thanks, dude!' ); })
.catch(() => { Alert.alert('Error!', 'Something went wrong!' ); });
}
render() {
this.upload();
return null;
}
}
So far, so easy. Let's test the positive POST behavior with a unit test where I mock the module:
A working unit test with a global mock:
import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import { configure, shallow } from 'enzyme';
import { ApisauceExample } from '../src/ApisauceExample';
configure({ adapter: new Adapter() });
const mockAlert = jest.fn();
jest.mock('Alert', () => ({
alert: mockAlert
}));
let mockPostResult = jest.fn();
jest.mock('apisauce', () => ({
create: () => ({
post: (url) => new Promise((resolve) => {
mockPostResult(url);
resolve();
})
})
}));
describe('ApisauceExample', () => {
it('should call create and alert', async () => {
await shallow(<ApisauceExample />);
expect(mockPostResult).toHaveBeenCalledTimes(1);
expect(mockAlert).toHaveBeenCalledWith('Success!', 'Thanks, dude!');
});
});
Yep, that works perfectly. But now, I want to test whether my code behaves properly in both a successful POST and an unsuccessful one. I learned that you can change the mocks with jest by using the mockImplementation
function. (see for example Jest Mock module per test).
I have already used that in other tests, but here it will simply not work:
Not working unit test with different mock behavior per test case
import React from 'react';
import Adapter from 'enzyme-adapter-react-16';
import { configure, shallow } from 'enzyme';
import { ApisauceExample } from '../src/ApisauceExample';
configure({ adapter: new Adapter() });
const mockAlert = jest.fn();
jest.mock('Alert', () => ({
alert: mockAlert
}));
jest.mock('apisauce', () => ({
create: jest.fn()
}));
import { create } from 'apisauce';
describe('ApisauceExample2', () => {
it('should call create and alert', async () => {
create.mockImplementation(() => ({
post: (url) => new Promise((resolve, reject) => {
reject("error");
})
}));
await shallow(<ApisauceExample />);
expect(mockAlert).toHaveBeenCalledWith('Error!', 'Something went wrong!');
});
});
This leads to:
Failed: Cannot read property 'post' of undefined
TypeError: Cannot read property 'post' of undefined
at ApisauceExample.upload (src/ApisauceExample.js:10:4)
at ApisauceExample.render (src/ApisauceExample.js:16:6)
And I just don't understand what is going on. In my opinion, the second mock is the same as the first one (except for the reject instead of the resolve).
Do you see what is going on here? I'd really appreciate any hints.
Thanks so much and have a nice day!