React-testing-library mock function not called - jestjs

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();
});

Related

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();
});

Unit test for Svelte on:click event

Can anybody point me into right direction?
I'm expecting mock function to be called after click event was fired.
What I've got is:
Expected number of calls: 1
Received number of calls: 0
This are my components along with test file:
EventTestingWrapper.svelte
<script>
export let testComponent
export let clickHandler
</script>
<div data-testid="testing-wrapper">
<svelte:component this={testComponent} on:click={clickHandler} />
</div>
Modal.svelte
<div
class="modal-background"
data-testid="modal-background"
on:click|self={close}
>
lorem
</div>
Modal.test.js
test('trying out test wrapper',()=>{
const clickHandler = jest.fn();
const { getByTestId } = render(EventTestingWrapper, {testComponent: Modal, clickHandler})
const modalBackground = getByTestId('modal-background')
const clickEvent = createEvent.click(modalBackground)
fireEvent(modalBackground, clickEvent);
expect(modalBackground).toBeInTheDocument()
expect(clickHandler).toHaveBeenCalledTimes(1)
})
I don't know exactly if this is what you want, but to pass on:click type event listeners to #testing-library/svelte rendered components you must use the component.$on syntax.
For example:
describe('Button', () => {
it('should render correctly', async () => {
const results = render(Button)
const onClick = jest.fn()
results.component.$on('click', onClick)
const button = results.container.querySelector('button')
expect(button).not.toBeNull()
// Using await when firing events is unique to the svelte testing library because
// we have to wait for the next `tick` so that Svelte flushes all pending state changes.
await fireEvent.click(button as HTMLElement)
expect(results.container).toBeInTheDocument()
expect(onClick.mock.calls.length).toEqual(1)
})
})
This link in the testing-library docs seemed to be quite useful https://sveltesociety.dev/recipes/testing-and-debugging/unit-testing-svelte-component/
This question is about jest but for people coming here in the future looking for a vitest solution, try this:
import Component from '$lib/MyComponent.svelte'
import { expect, test, vi } from 'vitest'
test(`invokes callback functions`, async () => {
const keyup = vi.fn()
const click = vi.fn()
const instance = new Component({
target: document.body,
props: { foo: `bar` },
})
// register callbacks (same as passing them as `on:<event>` props to component
instance.$on(`keyup`, keyup)
instance.$on(`click`, click)
const node = document.querySelector(`.some-css-selector`)
if (!node) throw new Error(`DOM node not found`)
node.dispatchEvent(new KeyboardEvent(`keyup`, { key: `Enter` }))
expect(keyup).toHaveBeenCalledTimes(1)
node.dispatchEvent(new MouseEvent(`click`))
expect(click).toHaveBeenCalledTimes(1)
})

How to mock/simulate a child component event for jest tests in svelte?

I want to run an isolate test on my svelte parent component (e.g. OrderSearch). Therefore the behavior of the child component (e.g. SearchForm) should be "simulated". The child component throws a search event that is bound in the parent component to initiateSearch.
SearchForm.svelte (Child component - NOT subject of testing - triggering of "submit" should be simulated)
<script>
const dispatchEvent = createEventDispatcher()
const submit = () => {
dispatchEvent('search', {firstName: '42'})
}
</script>
<div on:click="submit">Submit</div>
OrderSearch.svelte (Parent Component - Subject of testing)
<script>
let results = []
const initiateSearch = (e) => {
console.log('initiate search with', e)
// search is started and returns results
results = [{orderId: 'bar'}]
}
</script>
<SearchForm on:search="initiateSearch"></SearchForm>
{#each results as order}
<div data-testid="order">{order.id}</div>
{/each}
My not working approach so far when testing the OrderSearch.svelte in an isolated way:
OrderSearchTest.js
const {getAllByTestId, component} = render(Component)
expect(getAllByTestId('order')).toHaveLength(0)
await component.getSubComponent('SearchForm').dispatchEvent('search', {detail: {orderId: 'jonine'}}
expect(getAllByTestId('order')).toHaveLength(1)
Don't mock the child's event. You don't need to test that the on:<event> directive works, I would assume that Svelte has corresponding tests to ensure that it does. You need to test only that your component responds in the way that it should when the code that is executed on a particular event occurs. So trigger the event using fireEvent and mock or spy on a function, or function(s) called in the event handler.
Here's an example with appropriate changes made to your component:
OrderSearch.svelte
<script>
import http from "path/to/some/http/util"
let results = []
const initiateSearch = async (e) => {
// search is started and returns results
results = await http.fetchSearchResults(e.detail)
}
</script>
<SearchForm on:search="initiateSearch"></SearchForm>
{#each results as order}
<div data-testid="order">{order.id}</div>
{/each}
Then the corresponding test could look like:
import mockHttp from "path/to/some/http/util"
jest.mock("path/to/some/http/util")
...
it("calls search endpoint and renders results after fetch resolves", async () => {
mockHttp.fetchSearchResults.mockResolvedValue([{orderId: 'bar'}])
const { queryByText, getAllByTestId } = render(Component)
const submit = queryByText("Submit")
expect(getAllByTestId('order')).toHaveLength(0)
await fireEvent.click(submit)
expect(getAllByTestId('order')).toHaveLength(1)
})

How to mock a function and expect it to be called?

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

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