Jest testing :mockResolveValue with delay - testing

How to mockReturnValue with delay in jest testing?
The intention of this test is to mock method as unsettled on flushpromise. Let's say,
If we have promise which is mocked on test class. Once flushpromise , It would resolve the promise and we can able to assert the statement either as resolved or rejected.
Requirement for testclass is that the promise shouldn't be resolved or rejected after flushpromise (test Unsettled).
There are two possible way , we can achieve this by
making mockReturnValue return empty promise.
using jeskfaketimer to delay the mockReturnvalue
Both the above option is expected to give unsettled promise on assertion.
Although, The second option doesn't seem working with following code. Any input is appreciated.
Tried the following code.
`
import callout from ".../callout.."
jest.mock(
"../callout..",
()=>{
return{
default:jest.fn(),
};
},
{virtual: true}
);
const { setImmediate } = require("timers");
function flushPromises()
{
return new Promise((resolve)=> setImmediate(resolve));
}
`it("test delay", async()=>{
jest.useFakeTimers();
callout.mockResolvedValue(()=> Promise(resolve=>setTimeout(()=>resolve(),2000)));
jest.advanceTimersByTime(20);
await flushpromises();
expect(callout).not.toHavebeencalled();
jest.advanceTimersByTime(2000);
expect(callout).toHavebeencalled();
});
`

I am able to resolve the issue with the following change. Basically, I replaced the setTimeout with a wait custom function as shown below.
import callout from ".../callout.."
jest.mock( "../callout..",()=>{
return{
default:jest.fn(),
};
},
{virtual: true}
);
const { setImmediate } = require("timers");
function flushPromises()
{
return new Promise((resolve)=> setImmediate(resolve));
}
it("test delay", async()=>{
jest.useFakeTimers();
let response= {statusCode : 200,
JSON.stringify({status:200,data:true})};
callout.mockReturnValueOnce(wait(2000,respone);
jest.advanceTimersByTime(20);
await flushpromises();
expect(callout).not.toHavebeencalled();
jest.advanceTimersByTime(2000);
expect(callout).toHavebeencalled();
});
function wait(ms,value)
{
return new Promise(resolve => setTimeout(resolve,ms,value));
}

Related

Async lifecycle function in Vue Test Utils

When I tried to test the component which has mounted method like this:
mounted(){
this.search()
}
methods:{
async search(){
try{
await axios.something
console.log("not executed only when shallowMount")
}catch{}
}
}
I checked it returned Promise<pending> without await.
I wrote the test like this:
wrapper = await shallowMount(Component, {
localVue
});
await wrapper.vm.search()// this works perfectly
However, only the shallowMount apparently skips awaited function while the next line works perfectly.
I have no idea about this behavior.
How can I fix it?
Edit:
I also use Mirage.js for mocking response.
function deviceServer() {
return createServer({
environment: "test",
serializers: {
device: deviceListSerializer()
},
models: {
device: Model
},
fixtures: {
devices: devices
},
routes() {
this.namespace = "/api/v1/";
this.resource("device");
},
seeds(server) {
server.loadFixtures("devices");
}
});
}
shallowMount is not returning Promise and that's why there is nothing to await. To await promises in tests try to use flush-promises library. With flushPromises your test will look like this:
import flushPromises from 'flush-promises'
wrapper = shallowMount(Component, {
localVue
});
await flushPromises(); // we wait until all promises in created() hook are resolved
// expect...

Unit test jest enzyme throws error on Formik 'resetForm'

I am trying to run unit test (enzyme) throws error on Formik 'resetForm'.
TypeError: Cannot read property 'resetForm' of undefined
FormikForm.js
_handleSubmitPress = (values, { resetForm }) => {
const { onSubmit } = this.props;
if (onSubmit) {
onSubmit({ ...values, resetForm });
}
};
UnitTest.js:
it('Should fire formik form submit', () => {
const UpdateButtonPressMock = jest.fn();
const component = Component({
onSubmit: UpdateButtonPressMock,
});
expect(component.find(Formik)).toHaveLength(1);
component.find(Formik)
.first()
.simulate('Submit');
expect(UpdateButtonPressMock).toHaveBeenCalled();
});
I couldn't find any solution for this error.
Could someone help me on the above? I would really appreciate any help.
According to official docs for simulate, the function signature accepts an optional mock event.
The code you are testing uses properties that are not included in the default SyntheticEvent object that ReactWrapper passes to your event handler by default, for instance event.resetForm.
One way to do this is by triggering Formik's onSubmit directly like so:
// UnitTest.js
.simulate("submit", { resetForm: whateverYourMockResetFormValueShouldBe })
component.find(Formik)
.first()
.prop('onSubmit')(valuesMock, { resetForm: UpdateButtonPressMock });
expect(UpdateButtonPressMock).toHaveBeenCalled();
I haven't tested this, but you should be able to pass the event along with simulate as well.
// UnitTest.js
component.find(Formik)
.first()
.simulate("submit", { resetForm: UpdateButtonPressMock })
expect(UpdateButtonPressMock).toHaveBeenCalled();

Jest Testing in Vue

How would use Jest Test to test this method:
delayedFetch() {
setTimeout(() => {
this.fetchData();
}, 1000);
I have tried using Async and await but I'm prob using it wrong.
It's hard to test code with side effects, and you did not provide the entire context, but I try to help.
I think the this in the this.fetchData() inside the setTimeout is referencing the delayedFetch method itself. (I don't know it is your intention, as far as How I use vue.js
But anyway you can find, how to test setTimeouts here link to jest doc
Here is a simple implementation
const someObj = {
// I assume the delayedFetch is some method of an object
fetchData() {
return "some-data";
},
delayedFetch() {
const vue = this;
setTimeout(() => {
vue.fetchData();
}, 1000);
}
}
jest.useFakeTimers();
test("delayedFetchTest", () => {
someObj.delayedFetch();
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
})

How Test with Jest a function in the method "mounted" VueJS

I would to try call a function already mocked. I use vueJS for the frond and Jest as unit test. Below a example of my code. My purpose is to test the call of « anotherFunction". The first test is succeed , not the second.Thanks for help or suggestion
code vueJS:
mounted() {
this.myfunction();
}
methods: {
myfunction() {
this.anotherFunction();
}
}
Jest code:
describe('Home.vue', () => {
let wrapper = null;
const options = {
mocks: {
$t: () => 'some specific text',
},
methods: {
myFunction: jest.fn(),
},
};
it('Should renders Home Component', () => {
// Given
wrapper = shallowMount(Home, options);
// Then
expect(wrapper).toBeTruthy();
});
it('Should call anotherFunction', async (done) => {
// Given
wrapper.vm.anotherFunction = jest.fn().mockResolvedValue([]);
// When
await wrapper.vm.myFunction();
// THIS THE PROBLEM, myFunction is mocked and I can't call the function 'anotherFunction' inside...
// Then
// expect(wrapper.vm.anotherFunction).toHaveBeenCalled();
});
});
I was finding a good way to help you if this test case. So, I thought in something like the chuck code below:
import { mount } from '#vue/test-utils';
describe('Home', () => {
it('method calls test case', () => {
const anotherMethodMock = jest.fn();
wrapper = mount(Home, {
methods: {
anotherMethod: anotherMethodMock
}
});
expect(anotherMethodMock).toHaveBeenCalled();
});
});
But, the Jest threw the following exception:
[vue-test-utils]: overwriting methods via the methods property is deprecated and will be removed in the next major version. There is no clear migration path for themethods property - Vue does not support arbitrarily replacement of methods, nor should VTU. To stub a complex m ethod extract it from the component and test it in isolation. Otherwise, the suggestion is to rethink those tests.
I had the following insight, maybe, in this case, should be better to test the side effect of this anotherMethod calling. What does it change? Is something being shown to the user?
I believe that here we have started from the wrong concept.
I hope that this tip could be useful :)
As suggested by #Vinícius Alonso, We should avoid using methods and setMethods in our test cases because of it's deprecation. But you can still test the mounted lifecycle by mocking the functions that are being called during mount. So you can do something similar to below snippet.
describe('Mounted Lifecycle', () => {
const mockMethodOne = jest.spyOn(MyComponent.methods, 'methodOne');
const mockMethodTwo = jest.spyOn(MyComponent.methods, 'methodTwo');
it('Validate data and function call during mount', () => {
const wrapper = shallowMount(MyComponent);
expect(mockMethodOne).toHaveBeenCalled();
expect(mockMethodTwo).toHaveBeenCalled();
})
})
Do mount/shallowMount inside it only rather putting it outside of it as it was not working in my case. You can checkout more details on it if you want.

Testing a function called on an object with Jest in React Native

EDIT
Current example,
it('CALLED THE canOpenURL FUNCTION', () => {
const wrapper = mount(<ResourceCardComponent {...mockProps} />);
const canOpenURLSpy = jest.spyOn(Linking, 'canOpenURL');
wrapper.find('TouchableOpacity').simulate('click');
expect(canOpenURLSpy).toHaveBeenCalled();
canOpenURLSpy.mockReset();
canOpenURLSpy.mockRestore();
});
Error
expect(jest.fn()).toHaveBeenCalled() Expected mock function to have
been called.
Problem
I am using Jest & Enzyme to test a class made with React Native. This class has a function inside of it that when fired off uses the Linking library to call canOpenUrl and openUrl. I can simulate the click event on the mounted component but I am having trouble knowing how much of this I can actually test.
My goal is to check if Linking.canOpenUrl ever fires off.
Exmaple
The function inside the component looks like this,
onPressLink() {
console.log('HEY THIS FUNCTION FIRED WOOT WOOT');
Linking.canOpenURL(this.props.url).then((supported) => {
if (supported) {
Linking.openURL(this.props.url);
}
});
}
I can simulate this firing off like this,
describe('onPressLink has been called!', () => {
it('It clicks the mock function onPressLink!', (done) => {
const wrapper = mount(<MyComponent {...mockProps} />);
const onPressLink = jest.fn();
const a = new onPressLink();
wrapper.find('TouchableOpacity').first().simulate('click');
expect(onPressLink).toHaveBeenCalled();
done();
});
});
Now that does work, but my goal is to use something like this,
expect(Linking.canOpenUrl).toHaveBeenCalled();
But I keep getting this error,
TypeError: Cannot read property '_isMockFunction' of undefined
Current code that is trying to check if this function is ever fired off. Which is inside the parent function that is clicked with the simulate method,
it('calls canOpenURL', () => {
const wrapper = mount(<MyComponent {...mockProps} />);
const canOpenURL = jest.spyOn(wrapper.instance, 'onPressLink');
wrapper.find('TouchableOpacity').simulate('click');
expect('Linking.canOpenUrl').toHaveBeenCalled();
});
Question
What is the proper way to check to see if Linking.canOpenURL is fired when its parent function is executed?
(Since Jest 19.0.0+)
You can spy on the Linking module methods using jest.spyOn().
(1) Tell jest to spy on the module method:
const spy = jest.spyOn(Linking, 'canOpenURL');
(2) After doing everything you need to test it, check the spy:
expect(spy).toHaveBeenCalled();
(3) Clean up and stop spying on the module method
spy.mockReset();
spy.mockRestore();
If you don't want the tests to use the actual implementation of the methods, you can fake them like this:
jest.spyOn(Linking, 'canOpenURL').mockImplementation(() => Promise.resolve());
Where the function passed to mockImplementation will be whatever you want the method to do when called.
Ref https://facebook.github.io/jest/docs/en/jest-object.html#jestspyonobject-methodname
When using the actual implementation of your module method, which is asynchronous, the promise might not have been resolved by the time you tested it. You need to make sure any promise is resolved in your method implementation before making any assertions on it.
One way to deal with this is using async/await, like so:
it('...', async () => {
// Wait for promise to resolve before moving on
await wrapper.instance().onPressLink();
// make your assertions
expect(...);
});
Another option is using expect().resolves, available since Jest 20.0.0, where you wait for some promise in the argument to expect() to resolve with a value before making an assertion on that value.
expect(somePromiseThatEventuallyResolvesWithValue).resolves.toBe(Value);
I've done in simplest way:
Steps to spy:
Make spy object for original function using jest
Call original function with / without argument(s)
Assert the function which should be called with valid argument(s)
Reset mock
Restore mock
Here is the sample example
DefaultBrowser.ts which is actual class.
import { Linking } from 'react-native';
export const openDefaultBrowser = async url => {
if (await Linking.canOpenURL(url)) {
Linking.openURL(url);
}
};
DefaultBrowser.test.ts which is test case class.
import { openDefaultBrowser } from '../DefaultBrowser';
import { Linking } from 'react-native';
describe('openDefaultBrowser with validate and open url', () => {
it('validate url', async () => {
const spy = jest.spyOn(Linking, 'canOpenURL');
openDefaultBrowser('https://www.google.com');
expect(spy).toBeCalledWith('https://www.google.com');
spy.mockReset();
spy.mockRestore();
});
it('open url', async () => {
const spy = jest.spyOn(Linking, 'openURL');
openDefaultBrowser('https://www.google.com');
expect(spy).toBeCalledWith('https://www.google.com');
spy.mockReset();
spy.mockRestore();
});
});
Hope this helps you.
it('open url', async () => {
jest.spyOn(Linking, 'canOpenURL')
const spy = jest.spyOn(Linking, 'openURL')
openURL(sitePath)
await waitFor(() => {
expect(spy).toHaveBeenCalledWith(sitePath)
})
spy.mockReset()
spy.mockRestore()
})