问题
I see the changes the method makes as event handler, but jest.spyOn(wrapper.vm, 'methodName')
doesn't catch the call in major cases, but in some tests it somehow works. I suspect a possible re-render (maybe, due to options
argument in mount()
call), that replaces the original method with the jest.spyOn()
, because, if I trigger event twice in a row, toHaveBeenCalled()
fulfills and tracks the call.
The component:
<template>
<v-app-bar
app
color="primary"
dark
class="header"
>
<router-link
:to="{name: 'User'}"
class="up-and-down-link"
>
<v-toolbar-title>Тестовое задание</v-toolbar-title>
</router-link>
<v-spacer />
<v-toolbar-items>
<v-btn
v-if="isLoggedIn"
class="button-log-out"
text
@click="showLogOutModal"
>
Выйти из профиля
</v-btn>
</v-toolbar-items>
<Modal
v-bind="modal"
@close="onModalClose"
@click:outside="onModalClose(false)"
>
Вы уверены, что хотите выйти из профиля?
</Modal>
</v-app-bar>
</template>
<script>
import { LOG_OUT } from '@/store/auth/action-types';
import { IS_LOGGED_IN } from '@/store/auth/getter-types';
import Modal from '@/components/Modal.vue';
export default {
name: 'Header',
components: { Modal },
data() {
return {
modal: {
show: false,
withConfirmation: true,
},
};
},
computed: {
isLoggedIn() {
return this.$store.getters[`auth/${IS_LOGGED_IN}`];
},
},
methods: {
showLogOutModal() {
this.modal.show = true;
},
onModalClose(confirmation = false) {
this.modal.show = false;
if (confirmation === false) return;
this.logOut();
},
logOut() {
this.$store.dispatch(`auth/${LOG_OUT}`);
this.$router.push({ name: 'Login' });
},
},
};
</script>
And that test works as expected:
it('After confirmation of the action user is logged out and redirected to login page', async () => {
const actions = {
[LOG_OUT]: jest.fn(),
};
await store.hotUpdate({ modules: { auth: { ...auth, actions } } });
const mockedRouter = {
push: jest.fn(),
};
const wrapper = createWrapper(Header, {
data: () => ({ modal: { show: true } }),
mocks: {
$route: {},
$router: mockedRouter,
},
});
const mockedLogOutMethod = jest.spyOn(wrapper.vm, 'logOut');
await wrapper.find('.button-yes').trigger('click');
// the method's called
expect(mockedLogOutMethod).toHaveBeenCalled();
// the method performs log out and redirect
expect(actions[LOG_OUT]).toHaveBeenCalled();
expect(mockedRouter.push).toHaveBeenCalledWith({ name: 'Login' });
});
But this one doesn't, though during it the data
of the component is changed and we see it on the component's child prop ('show'
turns to be true
), thus the method is called indeed, but toHaveBeenCalled()
can't detect the fact:
it("Show modal with confirmation when 'log out' button was clicked", async () => {
const wrapper = createWrapper(Header);
const mockedShowModalMethod = jest.spyOn(wrapper .vm, 'showLogOutModal');
const modal = wrapper.findComponent(Modal);
// the modal is hidden initially
expect(modal.props('show')).toBe(false);
await wrapper.find('.button-log-out').trigger('click');
// after 'log out' button is clicked the modal appears
expect(modal.props('show')).toBe(true);
expect(mockedShowModalMethod).toHaveBeenCalled();
expect(wrapper.find('.modal-confirmation').exists()).toBe(true);
});
I found a workaround for that:
...
const mockedShowModalMethod = jest.spyOn(Header.methods, 'showLogOutModal');
const wrapper = createWrapper(Header);
....
However, I wanna find out the reason of this behavior, what do I miss here? I have another test suites, where my initial assertion with jest.spyOn()
works out as well as in the first test here.
回答1:
The difference between logOut
and showLogOutModal
methods is how they are used.
logOut
is called as regular method:
this.logOut();
And showLogOutModal
is passed as a callback:
<v-btn
v-if="isLoggedIn"
class="button-log-out"
text
@click="showLogOutModal"
this.showLogOutModal
is mocked with jest.spyOn
after this.showLogOutModal
is read to to be used as event handler, so a spy doesn't affect anything.
This would likely be fixed by forcing a re-render after replacing wrapper.vm.showLogOutModal
with a spy:
const mockedShowModalMethod = jest.spyOn(wrapper.vm, 'showLogOutModal');
await wrapper.vm.$forceUpdate();
But a workaround listed in the question is the recommended way to do this:
const mockedShowModalMethod = jest.spyOn(Header.methods, 'showLogOutModal');
const wrapper = createWrapper(Header);
来源:https://stackoverflow.com/questions/61859423/why-doesnt-jest-spyon-sometimes-work-on-a-vue-components-method