-
Notifications
You must be signed in to change notification settings - Fork 244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Spying on methods when using the composition api #775
Comments
I think you should be able to do something like: const wrapper = mount(ComponentName);
const spy = (wrapper.vm.methodName = jest.fn());
await wrapper.get('button').trigger('click');
expect(spy).toHaveBeenCalled(); Feel free to reopen this issue with a concrete example if that doesn't work, but I think I've used this pattern several times with success. |
Hello, thanks for looking into this. I created an example project with tests to demonstrate the issue (https://github.com/maurer2/vue3-testing-question). I have two two buttons that have click event handlers. One with parentheses and one without. <ButtonComponent
id="button-2a"
@click="handleClick1"
/>
<ButtonComponent
id="button-2b"
@click="handleClick2()"
/> The second click handler can be tested like so: it('button2 triggers counter update function when clicked', async () => {
const spy = jest.spyOn(cmp.vm, 'handleClick2');
await cmp.find('#button-2b').trigger('click');
expect(spy).toHaveBeenCalled();
}); This doesn't work for the first one as the following test fails: it('button1 triggers counter update function when clicked - spied on the fly', async () => {
const spy = jest.spyOn(cmp.vm, 'handleClick1');
await cmp.find('#button-2a').trigger('click');
expect(spy).toHaveBeenCalled();
}); This seems to only work when performing the spying before mounting the component like so: let cmp: any
let spyHandleClick1: any;
beforeEach(() => {
spyHandleClick1 = jest.spyOn((ComponentOptionsAPI.methods as any), 'handleClick1');
cmp = shallowMount(ComponentOptionsAPI, {});
});
it('button1 triggers counter update function when clicked - spied before mount', async () => {
await cmp.find('#button-2a').trigger('click');
expect(spyHandleClick1).toHaveBeenCalled();
}); Unfortunately Cheers |
Thanks for the use-case 👍 I sadly think we can't do much, as the generated code is fundamentally different between Is it really a problem to have to use parens if you want to spy on the handler? I'll reopen in the meantime, maybe @lmiller1990 or @afontcu will have an idea on how to handle this. |
Hello, thanks for reopening the issue. I guess it would make sense to always use parentheses when using the composition api and set I got a somewhat related question regarding the lifecycle methods. If I wanted to check if a function is called on mount, I would do it like before with the options api: let spyRunOnMount: any;
beforeEach(() => {
spyRunOnMount = jest.spyOn((ComponentOptionsAPI.methods as any), 'runOnMount');
mount stuff
});
it('run runOnMount runs on mount', async () => {
expect(spyRunOnMount).toHaveBeenCalled();
}); With the composition api I would need to return runOnMount function from setup to expose it in the test, but when I register the spy the lifecycle hook has already run and the test fails: let spyRunOnMount: any;
beforeEach(() => {
mount stuff
spyRunOnMount = jest.spyOn(cmp.vm, 'runOnMount');
});
it('runOnMount runs on mount', async () => {
expect(spyRunOnMount).toHaveBeenCalled();
}); Is there some way to test this or is just not possible with components that use the composition api? Cheers |
In that case what I usually really want to check is not that the method itself was called, but rather what it does or that an external dependency was properly called. For example, if I fetch some data on mount, I'll check that the composable that does the HTTP call was properly called: const mockRaceService = {
list: jest.fn()
};
jest.mock('@/composables/RaceService', () => ({
useRaceService: () => mockRaceService
}));
describe('Races.vue', () => {
test('should display every race', async () => {
const wrapper = mount(Races);
await flushPromises();
expect(mockRaceService.list).toHaveBeenCalled(); I hope this helps. I don't think we'll be able to allow to spy on the method as you would expect, because the model is fundamentally different. Or maybe with some hacky method, but I'm not really sure that's worth the complexity... We've been using the composition API for more than a year, and we manage to test everything in our projects. |
Okay, makes sense. Thanks for the explanation. Cheers |
You're welcome! If you don't mind, I'll close the issue for now, and we can revisit if some other use cases come up. |
It doesn't work on my side. MyComponent.vue <template>
<div @click="methodA"> </div>
</template>
<script setup lang="ts">
const methodA = () => {
console.log("methodA");
methodB();
};
const methodB = () => {
console.log("methodB");
};
</script> MyComponent.spec.ts import { mount } from "@vue/test-utils";
import MyComponent from "./MyComponent.vue";
it("a", () => {
const wrapper = mount(MyComponent);
const spy = jest.spyOn(wrapper.vm, "methodB");
wrapper.vm.methodA();
expect(spy).toHaveBeenCalled();
}); => Error
|
Hello, have you tried adding the parentheses to the event handlers? Vue permits both syntaxes, e.g.
If that doesn't fix the issue, you could turn it into a hook/composable. |
Why is there no way to spy on a Composition API method before mount? I am having to go on a major refactoring spree to bypass this limitation. Please don't tell me it is for my own good :) |
This is a limitation for I don't understand why you need a major refactoring spree - can you elaborate? If you are only refactoring and you need to make make changes to your test code, that's probably a good indication you are testing implementation details (like spying on a specific method). |
I have a component method A that calls another method B only when certain conditions are met. I want to test that by spyOn B. Does not work. So I made it a Composable. My app worls fine. My tests do not. When I export both A & B into my test. Set up the condition & call A. spyOn does not report that B is called. |
What is the effect of calling |
Let me try defineExpose. Have not used it before. The module is not a vue module, it is just a js file. I guess I will have to change that. sub1() actually calls the database and pulls record out of it async. I have that mocked already. |
defineExpose should not change anything, as Vue Test Utils bypasses it to make tests simpler. If it does, please open an issue. If you already mock the database, then the easiest way to test if everything work is to assert that your mocked database is properly called instead of sub1. See my comment above #775 (comment) |
This also adjusts event handlers to include parentheses (e.g., `fn` to `fn()`), allowing function spying during testing due to a limitation in the `vue-test-utils` library. vuejs/test-utils#775 (comment)
Hello, it seems that when using the options api, it is recommended to create spies for methods before mounting to avoid issues with event handlers that don't have braces (example 1, example 2, example 3).
How would one do the same with the composition api? The following doesn't work, as methods isn't available/exposed when using the composition api?
Thanks
Cheers
The text was updated successfully, but these errors were encountered: