How to mock a function and expect it to be called? - node.js

I am new to react-testing-library and I have been trying to test one function for a long time.
for example, I want to check if when a button is clicked a given function is called and it's throwing errors. so any help would be highly appreciated and if possible share with me any helpful resources.
signin.js
export default class SignIn extends Component {
constructor(props) {
super(props);
this.state = {
};
}
handleClose = (event, reason) => { };
validate = () => { };
change = (e) => { };
onSubmit = (e) => { };
render() {
return (<div>...</div>);
}
}
Full: https://github.com/blaise82/react-testing-library-try/blob/master/src/views/SignIn.js
this is my test
it('should submit form', async () => {
const { getByLabelText, getByText, container, debug } = render(<SignIn />);
const change = jest.fn();
const onSubmit = jest.fn();
const email = getByLabelText('email');
const password = getByLabelText('password');
const submit = getByLabelText('submit');
userEvent.type(email, 'octopusbn#gmail.com');
expect(email.value).toBe('octopusbn#gmail.com');
expect(password.value).toBe('');
expect(change).toHaveBeenCalled();
console.log(password)
await userEvent.click(submit);
expect(onSubmit).toHaveBeenCalled();
});
Full: https://github.com/blaise82/react-testing-library-try/blob/master/src/test/signin.test.js
results
> Expected number of calls: >= 1
> Received number of calls: 0
please let know what I am doing wrong.
Full code on GitHub: https://github.com/blaise82/react-testing-library-try

You can test a function by mocking all that is coming from outside of the component (aka dependencies) like - a prop callback, an external library api etc.
Before starting, let's go through what all functions are in the component.
Going through the component, I can list them as below:
Event handlers on elements [like handleClose, onSubmit, change in the component]
Functions internal to the component which do not interact with the state/functions outside the component [validate]
prop functions/library apis being called [axios.post]
Let's discuss them one by one --
Event handlers &
Functions internal to component not interacting with state/functions outside of the component
==> Event handlers that are attached to elements can safely be expected to get called. You don't need to test them if they are called. Rather, what you should test is the after-effect of them being called. Also the functions like validate
Let's take example of the change function that you are trying to test. This function after being called sets the state and the state gets reflected into the form elements. We can assert values of the form elements with a helper like this.
prop functions/library apis being called [axios.post]
==> These functions can be mocked and tested for the number of calls/parameters they are called with.
https://jestjs.io/docs/en/mock-functions.html#mocking-modules
In addition to the snippet of mocking jest as given in the link above, in your case -
axios.post.toHaveBeenCalledWith(expectedParms);
Also you can make it return results/errors you want and test respective component behaviour.
Hope you find this helpful. Cheers!

I think this is because you are not actually passing in your mocked functions to the component. You're just instantiating two constants that happen to have the name of the functions you're trying to watch, but are not actually being used anywhere in your component.
It sounds like you want to spy on your component's internal functions to see that they've been called.
Here's something of an example (not tested) based on a post (linked below) that might help you.
describe('spying on "onSubmit" method', () => {
it('should call onSubmit when the button is clicked', () => {
const wrapper = shallow(<SignIn />);
const instance = wrapper.instance();
jest.spyOn(instance, 'onSubmit');
wrapper.find('button').simulate('click');
expect(instance.onSubmit).toHaveBeenCalled();
});
});
Post: https://bambielli.com/til/2018-03-04-directly-test-react-component-methods/#spying-on-incrementcounter

Related

Playwright - How to access TestInfo outside of test function?

In Playwright, is there any way to access current TestInfo outside of a test function?
I have some tests that need access to the current testInfo.snapshotDir. However, the 'e_compareImages' function that needs the testInfo is nested several layers deep. It is not practical to pass the testInfo from the test function all the way to the function that needs that info.
I know e_compareImages() already has some access to the current testInfo because the function calls toMatchSnapshot() (from Jest's expect library), and the function pulls the correct file from the current testInfo.snapshotDir
Is there any way for functions in the actionHelper file to access current testInfo outside of an expect assertion?
I've posted what I believe are the relevant portions of the files below.
// LoginPage.test.js File
const { test } = require('#playwright/test');
test("Verify logo on Login page", async () => {
await loginPage.verifyLogo();
});
// actionHelper.js File
const { expect } = require('#playwright/test');
async e_compareImages(selector, expectedImageFileName) {
let locator= await this.page.locator(selector);
const actualImage = await locator.screenshot({scale: 'device'});
await expect(actualImage).toMatchSnapshot(expectedImageFileName);
}
I've tried importing Playwright's globals.js library and using its currentTestInfo(), but I have been unable to get it to work.
Is there another library or a way to extend an existing library that would do what I need?
Please let me know if you need any additional information.
You can access TestInfo object outside of a test function using the test method's second argument:
async e_compareImages(testInfo, selector, expectedImageFileName) {
// ...
const actualImage = await locator.screenshot({scale: 'device'});
await expect(actualImage).toMatchSnapshot(testInfo, expectedImageFileName);
}
Now, when you call e_compareImage function you can pass the TestInfo object as an argument:
test("Verify logo on Login page", async (testInfo) => {
await loginPage.verifyLogo(testInfo);
});
I found what I was looking for. You can use the below code to require the globals library
var _globals = require("../node_modules/#playwright/test/lib/globals");
Then you can use this function to get the current testInfo object directly (without having to pass the object through however many intermediary functions)
// Get the current test's testInfo object
async getCurrentTestInfo() {
const curTestInfo = await (0, _globals.currentTestInfo)();
return await curTestInfo
}

How to do callback in our component using react jest test cases

How can we do callback on success and failue cases for below lines of code for test coverage using jest
const handleService = () => {
window.domain.service("1321",'',onSuccess, onFailure)
}
const onSuccess = () => {
....update state values
}
const onFailure = () => {
....update state values
}
Something like this:
Spy on window.domain.service to gain access to the calls it receives. This will allow you to access the parameters of those calls which will be "1321",'',onSuccess, onFailure
Assign the function you wish to test to a variable
Invoke the function to execute the code in it (this will get you the coverage)
(Optional) assert that the callback functions behave correctly
Here is a snippet to help demonstrate
it('should run', () => {
// Some setup to create the function on the window, may not be needed if done elsewhere.
// Could be good to do this in a beforeEach and clean up in afterEach to avoid contaminating the window object
window.domain = {
service: () => {},
}
// Spy on the window.domain.service method.
// Provide a mock implementation if you don't want the real one to be called
const serviceSpy = jest.spyOn(window.domain, 'service');
executeYourCode();
// capture the arguments to the call
const [_arg1, _arg2, onSuccess, onFailure] = serviceSpy.mock.calls[0];
// execute the callbacks
onSuccess();
onFailure();
});

Jest/javascript testing library mock is not firing when clicking on a div onclick on the method that is mocked

SETUP:
I am doing an integration test. Have the parent component that brings in two child components. On of the child components has an onClick that calls a method IN that child component file. I export that method, mock it.
ISSUE:
In the test, I am finding the "div" with the onclick, fire an event. But the toHaveBeenCalledOnce is not showing it has been called. The actual method IS called when I fire the event, but the "spyon" is not actually spying it.
Code:
// import the the file to be mocked, This is a child component
import * as ProductList from '../ProductList';
// Mock the exported method that I have OUTSIDE the functional component.
test('Div click', async () => {
render(<Products />);
const divToClickAdd = await screen.findByTestId('item17');
// this doesn't appear to spyOn it.
const jsonSpy = jest.spyOn(ProductList, 'myMethodToFireAnAction');
act(() => user.click(divClick));
// this fails as it is called 0 times
// I also added a waitFor thinking it was a timing thing, still didn't work
await expect(jsonSpy).toHaveBeenCalledTimes(1);
}
CHILD COMPONENT FILE: psuedo code - ProductListing
export const myMethodToFireAnAction = (id) => {
// do some stuff. If I comment in here, it does fire.
};
// This is the child component. It renders a list of divs that have an onclick calls that
// method above and passes the id.
const ProductListing = (products) => {
That inner method is considered implementation details, and we want to avoid testing those. Instead, test what functionality changed for the user by firing that method, e.g. if it introduced a new DOM node, changed a style of an element, etc.
If you really need to test that method, you could pass it as a prop to the component, and in the test you do:
const onClickMock = jest.fn()
render(<Component onClick={onClickMock} />)
// fire click event
expect(onClickMock).toHaveBeenCalledTimes(1)

React-testing-library mock function not called

i'm trying to test my component:
in my componet when i click to button i call some function and pass there some id.
my test:
it("should call function on submit click", async () => {
const wrapper = render(<Component />);
const id = "1";
const handleSubmit = jest.fn();
const SubmitBtn = wrapper.getByTestId("submit-btn");
fireEvent.click(SubmitBtn);
await waitFor(() => wrapper);
expect(handleSubmit).toHaveBeenCalled();
});
here i tried:
expect(handleSubmit).toHaveBeenCalled();
expect(handleSubmit).toHaveBeenCalledWith(id);
expect(handleSubmit).toBeCalled();
expect(handleSubmit).toBeCalledWith(id)
tried everything but it's not works..
expect(jest.fn()).toBeCalled()
Expected number of calls: >= 1
Received number of calls: 0
Your mock cannot be called this, because you declare it after having rendered your component. I assume you have a handleSubmit function in your component that gets called on submit click, but when you call :
const handleSubmit = jest.fn();
...the component is already renderer and there is no way you can mock its handleSubmit function.
What I suggest :
Do not try to spy on handleSubmit function, test like a real user
You should not try to test internal component logic, but test what a real user would see : a loading spinner, a confirmation message...This is what promotes RTL, because it will give you confidence in your test, decouple your test from internal implementation (which is a good thing when it comes to React components unit testing), and force you as a developper to think about user first when writing code (thinking how you will test your code using RTL forces you to think about user experience).
In your test you could do this :
// in component
const handleSubmit = () => {
// do things
// say that this boolean controls a message display
setSubmittedMessageDisplay(true);
}
In test
it('should call function on submit click', async () => {
const wrapper = render(<Component />);
const submitBtn = wrapper.getByTestId('submit-btn');
fireEvent.click(submitBtn);
expect(wrapper.getByText('form submitted - controlled by submittedMessageDisplay')).toBeTruthy();
});
Pass the form submit handler down from parent component, mock it
This implies to lift things up to the parent component :
// in parent
const handleSubmit = () => {...}
<ChildWithForm onSubmit={handleSubmit} />
// in child
<Form onSubmit={props.onSubmit}>...</Form>
Then in your test you can do this :
test('submits form correctly', () => {
const mockedHandler = jest.fn();
const wrapper = render(<Child onSubmit={mockedHandler} />);
const submitBtn = wrapper.getByTestId('submit-btn');
fireEvent.click(submitBtn);
expect(mockedHandler).toHaveBeenCalled();
});

How do I make my vue-unit-test trigger a click event on v-btn in Vuetify?

I am creating some unit test for my component, but the test keeps failing, since the button I'm testing
keeps not getting triggerd by a click-event.
I've used the docs as a foundation for my test: https://vuetifyjs.com/sv-SE/getting-started/unit-testing/
I've also tried some of the suggestions mentioned here: https://forum.vuejs.org/t/how-to-trigger-an-onchange-event/11081/4
But it seems like I'm missing something, anyone who can help me out?
My test:
test('If you can click on the Test button', () => {
const wrapper = shallowMount(myComponent, {
localVue,
vuetify,
});
const event = jest.fn();
const button = wrapper.find({name: 'v-btn'})
expect(button.exists()).toBe(true) //this works
wrapper.vm.$on('v-btn:clicked', event)
expect(event).toHaveBeenCalledTimes(0)
button.trigger('click')
expect(event).toHaveBeenCalledTimes(1)
})
myComponent:
<template>
<v-btn class="primary-text" #click.native="methodForTesting($event)">Test</v-btn>
<template>
<script>
methods: {
methodForTesting(){
console.log('button clicked!')
}
</script>
Hope this help, I changed your HTML a bit.
Firstly, I added a <div> and put <v-btn> inside it, this is very
important.
Secondly, I declared a data prop called index which is
initialized in 1.
Thirdly, I used data-testid="button" to identify
it and find it during test.
<template>
<div>
<v-btn data-testid="button" class="primary-text" #click="methodForTesting">Test</v-btn>
</div>
</template>
<script>
export default {
data() {
return {
index: 1
};
},
methods: {
methodForTesting() {
this.index++;
}
}
};
</script>
Now, for the unit test.
The key is to use vm.$emit('click') instead of .trigger('click') since v-btn is a component of Vuetify. If you were using button tag, then you can use .trigger('click').
Also, I changed how jest finds this button.
import Vuetify from 'vuetify'
// Utilities
import { mount, createLocalVue } from '#vue/test-utils'
// Components
import Test from '#/views/Test.vue';
// VARIABLES INITIALIZATION //
const vuetify = new Vuetify()
const localVue = createLocalVue()
// TESTING SECTION //
describe('Testing v-btn component', () => {
it('should trigger methodForTesting', async () => {
const wrapper = mount(Test, {
localVue,
vuetify,
})
const button = wrapper.find('[data-testid="button"]')
expect(button.exists()).toBe(true)
expect(wrapper.vm.$data.index).toBe(1)
button.vm.$emit('click')
await wrapper.vm.$nextTick()
expect(wrapper.vm.$data.index).toBe(2)
})
})
Now, when you are doing a unit test, you should check inputs and outputs. In this case, your input is the click event and your output, is not your method been called, but the data modified or sent by this method. That's why I declared index to see if it changes when you click the button.
Anyway, if you want to check if your method was called, you can use this code instead
describe('Testing v-btn component', () => {
it('should trigger methodForTesting', async () => {
const methodForTesting = jest.fn()
const wrapper = mount(Test, {
localVue,
vuetify,
methods: {
methodForTesting
}
})
const button = wrapper.find('[data-testid="button"]')
expect(button.exists()).toBe(true)
expect(methodForTesting).toHaveBeenCalledTimes(0)
button.vm.$emit('click')
await wrapper.vm.$nextTick()
expect(methodForTesting).toHaveBeenCalledTimes(1)
})
})
But you will receive the next error:
[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 the `methods` property - Vue does not support arbitrarily
replacement of methods, nor should VTU. To stub a complex method extract it from the component and test it in isolation. Otherwise, the suggestion is to rethink those tests.
This is my first post btw

Resources