1.0.0
It looks index.d.ts isn't updated yet. After upgrading to v1.0.0 I started to get deprecation errors, but methods like findAllComponents and findComponent are missed in index.d.ts.
Oops. We should add those definitions.
I can fix this later today. 🤦 and do 1.0.1 straight away.
Or if you have time before me you can send a PR and I can get it merged up.
Can someone please review? https://github.com/vuejs/vue-test-utils/pull/1530
Also I believe the new attachTo is missing in the MountOptions interface, could this be fixed too in 1.0.1?
@jmariller yep, I fixed that too. It should be fixed now: https://www.npmjs.com/package/@vue/test-utils 1.0.1 is out.
LMK if I missed anything.
@lmiller1990 thanks a lot for the quick fix & feedback!
I shortly checked and it seems something is still missing in the definitions for mount, I believe the following interface should be adjusted (e.g. attachedTo is missing):

Or am I missing/misunderstanding something?
I am new to TS, I probably messed up.
I will fix it right now.
Funny how no-one noticed bugs until 1.0 drops ):
https://github.com/vuejs/vue-test-utils/pull/1533/files#diff-b801fc35a5096ae10463a20e928fb818R144
Ok.... try getting 1.0.2 and see if that fixed it for you.
Thanks! If not, lmk and I'll look into it.
Works now! Thank you so much @lmiller1990 for the quick fix, really appreciate it!
And thanks in general for these utils, they are great :)
@lmiller1990 Now I see deprecations about methods and setMethods, but I can't find the solution to migrate. Can we add suggestions about how to fix that in addition to warnings also?
@Ky6uk hi! We removed setMethods in VTUnext because it might lead to flaky tests – your tests end up relying on implementation details. The suggestion is to rethink those tests, I believe!
@afontcu the test is very simple. I'm trying to $emit an action (click, for example), and checking a method has been called afterward, without any implementation details. Basically it just something like wrapper.setMethods({ onCick: mockedFn }) and mockedFn.hasBeenCalled(). So setMethods had been useful for that purpose.
Try using jest.spyOn
@dobromir-hristov on what property? It doesn't seem easy to avoid deprecation atm. The most convenient way regarding documentation to check a method without firing the implementation is to use setMethods.
it('triggers click', () => {
const mock = jest.fn();
const wrapper = shallowMount(Component);
wrapper.setMethods({ onClick: mock });
const linkWrapper = wrapper.findComponent({ name: 'Link' });
expect(mock).toBeCalledTimes(0);
linkWrapper.vm.$emit('click');
expect(mock).toBeCalledTimes(1);
});
So instead of checking whether your logic is correct and the right event is emitted or dom is changed from that click action, you just want to see if you are listening to it properly? Then write another separate test to actually test your logic? Isn't that double the work?
It just an example. The test itself may check that the emits will call the correct methods. For example, one click will call this method, double click will call another method, or the same method with a different argument, etc. And I shouldn't think about the implementation of the methods because of the logic inside too complicated and tested by different unit tests. That the idea. It's not double work, it's separating tests for complicated logic aside component tests.
Hm what about const spy = jest.spyOn(Component.methods, "yourMethod")
It doesn't work. Cannot spyOn on a primitive value; undefined given. I also thought about mocking wrapper.vm, but it seems read-only property. Also, it requires some crutches in TypeScript.
I'd say the component calling a method on an event is precisely an implementation detail, isn't it? The side-effects of this method (HTTP requests, cookies, other emitted events…) are the visible part of the interaction, and the one I'd test.
Can you provide a reproduction link (codesandbox or similar)? Happy to help.
@afontcu component can not be changed after an interaction. It can just trigger another logic under the hood. And my case is to be sure all triggers are calling the right methods in the component. That's it.
I have not used codesandbox before, I will check it's possible to add a quick test for that.
Upd: I've tried to create a sandbox, but it doesn't seem to work.
I've tried to create a sandbox, but it doesn't seem to work.
In this (contrived, of course!) example, I'd say that the interesting behaviour from the outside is that the component logs something after a click. For that, using a method or simply calling console.log from the template (or any other alternative) is an implementation detail, and something that, if changed, should not break your test.
In that test, I would suggest mocking/spying console.log, and assert it's been called right after triggering a click.
Yes, It makes sense. But it will also require to rewrite a bunch of tests before upgrading to vtu 2.0. The mocking methods directly still look useful in some cases to not remove them completely. Anyway, thanks to clarify.
I was led here from this thread: https://github.com/vuejs/vue-test-utils/issues/1536
Unfortunately, not being able to use the methods mounting object makes it so that testing components that make calls to methods during the mounted/created hooks a little more difficult.
In my particular case, I have a component that during created calls a method recursively. In my tests, I was able to resolve this issue my making use of the methods option:
const wrapper = shallowMount(Page, { methods: { recursivePoll: jest.fn() } })
I'm not sure how to proceed without the ability to override it for any of my mounted tests.
Hi @robokozo! That sounds interesting. Could you share a reproduction link or a repository to take a look at this use case?
@afontcu No I can't... how embarrassing ^^ I can't reproduce it in an isolated environment because of a bug in the original code I was referring to. It looks good! Keep it up the great work! (The vue testing team should consider opening themselves up for github sponsorships)
One immediately work around that comes to mind (maybe not ideal) would be to put said method in another file and import it. Then you could use jest.mock. You could use a mixin:
// ... before
created() {
this.recurse()
}
// ... after
import { recurse } from './mixin.js'
mixins: [recurse]
// ... test
const mockRecurse = jest.fn()
jest.mock('./mixin.js', () => ({
recurse: mockRecurse
}))
To do this, the variable passed into jest.mock must begin with mock or Jest will complain.
Maybe not ideal, but I think the situation you describe is one of the few edge cases where there is no direct migration path.
When you provide a minimal example, maybe we can find another option.
I have functions to render correct data used like this:
<tr>
<td><b>{{ $t('DASHBOARD.SEASON.END') }}</b></td>
<td>{{ convertServerZoneToUserZone(season.end, 'L') }}</td>
</tr>
convertServerZoneToUserZone function is from mixin. But i don't need to test it in component. Register it as jest.fn() was fast and useful. How to mock this function? Without mocking it i can not to render component and test it.
If it is from a mixin, you are doing import { convertServerZoneToUserZone } from './some-mixin.js'? Can you just mock the module?
jest.mock('./some-mixin', () => ({
convertServerZoneToUserZone: () => {}
}))
@lmiller1990 I was trying like that
import mixinMoment from '@/mixins/mixinMoment';
jest.mock('@/mixins/mixinMoment', () => ({
convertServerZoneToUserZone: jest.fn(),
}));
and then i got error:
[Vue warn]: Error in render: "TypeError: _vm.convertServerZoneToUserZone is not a function"
I add mixin to shallowMount, try 'mount' also, but it changes nothing
Assuming that mixins is a method you probably need to do:
jest.mock('@/mixins/mixinMoment', () => ({
methods: {
convertServerZoneToUserZone: jest.fn()
}
}));
@lmiller1990 :facepalm: of course it works now. Sometimes i feel so stupid ;) Thanks!
But i have another problem. I have button. After click it runs function. And that function runs another function. But this last function is from plugin. So i should spy on first function, and then mock function from plugin. But if i do that like this:
jest.mock('vue', () => ({
toasted: {
error: jest.fn()
},
}));
Vue stops working because of lack of Vue.use()
summary: Buttons runs funcion a(). Function a runs function b(), and function b runs toasted which i cant mock. How to handle that?
Please share the function that runs the plugin code
validate() {
let valid = true;
if (this.bet === null) {
valid = false;
this.$toasted.error(this.$t('GAMES.BETS.INVALID_NO_BET'));
}
if (this.stake < this.minBet || this.stake > this.maxBet) {
valid = false;
this.$toasted.error(
this.$t(
'GAMES.BETS.INVALID_STAKE',
{
minBet: this.minBet.toLocaleString(this.$i18n.realLocale),
maxBet: this.maxBet.toLocaleString(this.$i18n.realLocale)
}
)
);
}
return valid;
},
And the problem is this.$toasted.error(). If i mock vue module like
jest.mock('Vue', () => ({
toasted: {
error: jest.fn()
}
}))
This function is mocked. But i had errors like Vue.use is not a function etc. I broke whole instance then i think. But otherwise mocking many functions only for check if button runs function a() is not good in my opinion. Register function is fast end easy. I really don't understand why i cant use it.
This is not how jest.mock works. You cannot "partially" mock something like this - you either mock the entire module, or you don't.
Can you use mocks? eg:
const errorMock = jest.fn()
wrapper = mount(Foo, {
mocks: {
$toasted: {
error: errorMock
}
})
Most helpful comment
@afontcu No I can't... how embarrassing ^^ I can't reproduce it in an isolated environment because of a bug in the original code I was referring to. It looks good! Keep it up the great work! (The vue testing team should consider opening themselves up for github sponsorships)