How to jest.spyOn mock implementation only for the first call then use default implementation? - unit-testing

I would like to use jest.spyOn to mock the implementation of a method only for the first call. On 2nd 3rd ...nth call want to call it's actual previous implementation.
I tried the below in ts and it is not working:
import handler from '../src/handler';
import * as cRedis from '../src/redis';
jest.spyOn(cRedis, 'rGetAsync');
describe('handle cart quantity validation', () => {
test('test 1 blabla', async () => {
(cRedis.rGetAsync as jest.Mock).mockImplementationOnce(
() =>
new Promise(resolve =>
setTimeout(() => {
resolve('{}');
}, 1000),
),
);
const response = await handler();
});
});
the handler function calls the method rGetAsync of cRedis two times.
just for illustrative example:
handler.ts
import { rGetAsync } from './redis';
export default function () {
const a = await rGetAsync('a');
const b = await rGetAsync('b');
console.log(a, b);
}
My problem is that the mockedImplementation is used in both calls!
So mockImplementationOnce is not really mocking it once.
I expect that for first call to use the mock implementation and second one the real one.
How can I achieve this with jest?

I have used your code and the following test:
import * as cRedis from '../redis';
import handler from '../handler';
describe('handle cart quantity validation', () => {
it('test 1 blabla', async () => {
const spy = jest.spyOn(cRedis, 'rGetAsync');
spy.mockResolvedValueOnce('Not Original')
await handler();
});
});
My redis looks like this:
export const rGetAsync = async () => Promise.resolve('Original implementation');
And my output is:
console.log
Not Original Original implementation
So it works properly.
You can check my post about clear/reset/restore of spies and mocks.

Related

Jest test redux action with thunk doesn't cover statemets

Hello i have been trying to test a function with thunk and all the test passes but can't figure it out why the coverage doesn't not update or the test function does not cover the statement.
This is my function:
export const setFinished = (campaignId, userId, actionId, callback) => {
return async (dispatch, getState) => {
await axios.post(`http://bazuca.com:9000/campaigns/${campaignId}/progress`, {
userId,
actionId
}, { headers: { token: getState().app.token } })
.then((response) => {
})
.catch((error) => {
})
callback();
}
}
This is my last test (I have done like 3 different types and cant get the coverage to work)
describe("setFinished", () => {
it("works", () => {
const dispatch = jest.fn();
const callback = jest.fn(() => 'callback');
const getState = jest.fn();
let a = setFinished(1, 1, 1, callback)
expect(a).toHaveBeenCalledWith(1, 1, 1, callback);
a(dispatch, getState);
expect(callback).toHaveBeenCalled();
});
});
and i just get this in the coverage:
Maybe im doing it wrong? or should use another library?
There might be some things missing in your test setup. Especially the way you're making an assertion about the dispatch mock looks unusual. Without going into too much detail, just consider the following:
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { setFinished } from 'path/to/your/actions';
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('setFinished', () => {
it('works', () => {
// You have to make sure axios calls are mocked out properly
// at this point. I don't have a snippet handy for this so I
// left it out. But it would be similar to the following:
axios.mockImplementationOnce(() => ({
// Let the promise return whatever your response is for a
// positive test case
post: () => Promise.resolve({ isFinished: true })
}));
const expected = [
// I'm assuming something like this is dispatched in the
// .then handler of your action:
{ type: 'SET_FINISHED_SUCCESS' }
];
const store = mockStore({});
// Mock some arguments here
return store.dispatch(setFinished(1, 2, 3, () => null))
.then(() => expect(store.getActions()).toEqual(expected));
});
});
If axios is mocked out correctly, this will definitely achieve 100% coverage for this action if you also add a negative test case for the catch block.

Mocking default exported function with Jest says it wasn't called but it was

[EDIT - POSSIBLE SOLUTION]
So I realised that my componentWillMount is an async method since it is using an async fs wrapper to do fs operations. So I made the beforeEach function argument async and awaited on the Enzyme.shallow. This seems to have worked. It just came to me that if it's async maybe the lifecycle hadn't run yet when the expectation was ran... What do you think?
It now looks like this
// root/Meetings/__tests__/MeetingsScreen.test.js
...
import sortMeetings from '../../helpers/sort';
jest.mock('../../helpers/sort', () => jest.fn());
describe('MeetingsScreen', () => {
let wrapper;
const mockValueForMeetings = [];
sortMeetings.mockReturnValue(mockValueForMeetings);
beforeEach(async () => {
wrapper = await Enzyme.shallow(<MeetingsScreen />);
});
it('should call the sort method', () => {
expect(sortMeetings).toHaveBeenCalled();
});
});
[ORIGINAL QUESTION]
I am mocking an imported function and the test says that it wasn't called but it returns the stubbed value.
I have this class/screen in react-native that imports a helper file that only has one function to do a sort.
// root/helpers/sort.js
import moment from 'moment';
const compareDateTime = (a, b) => {
...
};
const sortMeetings = meetings => meetings.sort(compareDateTime);
export default sortMeetings;
My class looks like this
// root/Meetings/MeetingsScreen.js
...
import sortMeetings from '../helpers/sort';
export default class MeetingsScreen extends Component {
...
componentDidMount() {
this.updateState();
}
updateState = async () => {
const meetingsOnFile = await fsStorage.getItem('meetings'); // this is also stubbed and returns an [{}]
const meetings = sortMeetings(meetingsOnFile);
this.setState({ meetings });
}
render() {
return (
<MeetingList meetings={this.state.meetings} />
);
}
}
And this it my test. I am using Jest.
// root/Meetings/__tests__/MeetingsScreen.test.js
...
import sortMeetings from '../../helpers/sort';
jest.mock('../../helpers/sort', () => jest.fn());
describe('MeetingsScreen', () => {
let wrapper;
const mockValueForMeetings = [];
sortMeetings.mockReturnValue(mockValueForMeetings);
beforeEach(() => {
wrapper = Enzyme.shallow(<MeetingsScreen />);
});
it('should call the sort method', () => {
expect(sortMeetings).toHaveBeenCalled();
});
});
So If I got it right, since the import of the default function returns a function when using Jest i am mocking with a function. Before the test I am setting that mock to always return an empty array and I am printing out down the code after the call of the function (in the source code) and it indeed returns an array. I changed it to others values as well (i.e. 13, [2, 3, 4], et.c) and they all get returned. So I would assume that the mock function gets called. But the expectation fails. If I print the sortMeetings.mock as well, it just shows empty arrays in it's values {"calls":[],"instances":[],"timestamps":[]}
Could someone point to the mistake I am doing. I think I might be wrong about how import default functions work or how Jest is doing the mocking

Unit test with mocha + chai always passed

I try to use 'mocha' and 'chai' for my unit test but I have a problem with the test result, it always passed.
Please take a look.
UnitTest.spec.ts
import PostgresService from "../src/Services/PostgresService"
import {expect} from "chai"
import 'mocha'
describe('Postgres Override Test function', () => {
it('should return any number but not zero', async () => {
let client = new PostgresService();
let result = await client.getLatestCMD("Servo");
try{
console.log("Type : " + typeof(result));
console.log("Value : " + result.rows[0].id);
expect(result.rows[0].id).to.equal(0)
}catch(error){
}
})
})
Remove the try catch block to actually run your expect function.
If your try block returns an error the JavaScript interpreter moves on to the catch block and so the former is never run.
When you use ASYNCHRONOUS CODE, you need done in the callback
example
You need to declare done as an argument to it.
it('description', (done) => {
expect((err, result) => {
if (err) done(err);
else done();
})
})

Jest -- Mock a function called inside a React Component

Jest provides a way to mock functions as described in their docs
apiGetMethod = jest.fn().mockImplementation(
new Promise((resolve, reject) => {
const userID = parseInt(url.substr('/users/'.length), 10);
process.nextTick(
() => users[userID] ? resolve(users[userID]) : reject({
error: 'User with ' + userID + ' not found.',
});
);
});
);
However these mocks only seem to work when the function is called directly in a test.
describe('example test', () => {
it('uses the mocked function', () => {
apiGetMethod().then(...);
});
});
If I have a React Component defined as such how can I mock it?
import { apiGetMethod } from './api';
class Foo extends React.Component {
state = {
data: []
}
makeRequest = () => {
apiGetMethod().then(result => {
this.setState({data: result});
});
};
componentDidMount() {
this.makeRequest();
}
render() {
return (
<ul>
{ this.state.data.map((data) => <li>{data}</li>) }
</ul>
)
}
}
I have no idea how to make it so Foo component calls my mocked apiGetMethod() implementation so that I can test that it renders properly with data.
(this is a simplified, contrived example for the sake of understanding how to mock functions called inside react components)
edit: api.js file for clarity
// api.js
import 'whatwg-fetch';
export function apiGetMethod() {
return fetch(url, {...});
}
You have to mock the ./api module like this and import it so you can set the implemenation of the mock
import { apiGetMethod } from './api'
jest.mock('./api', () => ({ apiGetMethod: jest.fn() }))
in your test can set how the mock should work using mockImplementation:
apiGetMethod.mockImplementation(() => Promise.resolve('test1234'))
In case the jest.mock method from #Andreas's answer did not work for you. you could try the following in your test file.
const api = require('./api');
api.apiGetMethod = jest.fn(/* Add custom implementation here.*/);
This should execute your mocked version of the apiGetMethod inside you Foo component.
Here is an updated solution for anyone struggling with this in '21. This solution uses Typescript, so be aware of that. For regular JS just take out the type calls wherever you see them.
You import the function inside your test at the top
import functionToMock from '../api'
Then you indeed mock the call to the folder outside of the tests, to indicate that anything being called from this folder should and will be mocked
[imports are up here]
jest.mock('../api');
[tests are down here]
Next we mock the actual function we're importing. Personally I did this inside the test, but I assume it works just as well outside the test or inside a beforeEach
(functionToMock as jest.Mock).mockResolvedValue(data_that_is_returned);
Now here's the kicker and where everyone seems to get stuck. So far this is correct, but we are missing one important bit when mocking functions inside a component: act. You can read more on it here but essentially we want to wrap our render inside this act. React testing library has it's own version of act. It is also asynchronous, so you have to make sure your test is async and also define the destructured variables from render outside of it.
In the end your test file should look something like this:
import { render, act } from '#testing-library/react';
import UserGrid from '../components/Users/UserGrid';
import { data2 } from '../__fixtures__/data';
import functionToMock from '../api';
jest.mock('../api');
describe("Test Suite", () => {
it('Renders', async () => {
(functionToMock as jest.Mock).mockResolvedValue(data2);
let getAllByTestId: any;
let getByTestId: any;
await act(async () => {
({ getByTestId, getAllByTestId } = render(<UserGrid />));
});
const container = getByTestId('grid-container');
const userBoxes = getAllByTestId('user-box');
});
});
Another solution to mock this would be:
window['getData'] = jest.fn();

angular2 testing using jasmine for subscribe method

I have a spec code to test like this
it('login test', () => {
const fixture = TestBed.createComponent(component);
fixture.detectChanges();
let authService = fixture.debugElement.injector.get(Auth);
spyOn(authService, 'login').and.returnValue('');
const elements = fixture.nativeElement;
fixture.componentInstance.login();
expect(authService.login).toHaveBeenCalled();
});
and the implementation code like this
login() {
this.auth.login(this.username, this.password).subscribe(() => {
}
});
}
it gives error:
this.auth.login(...).subscribe is not a function
Why does this error happen?
You need to return something with a subscribe method, as the component calls subscribe directly from login. A string does not. You could just return an object with a subscribe function and it should work
and.returnValue({ subscribe: () => {} });
Or if you want to pass a real observable, you could
and.returnValue(Observable.of('some value'));
You might need to import rxjs/add/observable/of
On rxjs v6 you should use of instead of Observable.of or Observable.from e.g
const loginService: any = {
getUser: () => of(['Adam West']),
};
and import
import { of } from 'rxjs';
Change your spy for the 'login' method on your authService to return an observable instead of a value. You'll need to import:
import 'rxjs/add/observable/from';
import {Observable} from 'rxjs/Observable';
Setup your spy:
const loginResult = '';
const spy = spyOn(authService, 'login').and.callFake(() => {
return Observable.from([loginResult]);
})
Call login:
fixture.componentInstance.login();
Assert:
expect(spy).toHaveBeenCalled();