-
Notifications
You must be signed in to change notification settings - Fork 664
Description
Subject of the issue
When using TypeScript in Vue.js project and Jest for testing, I'm trying to implement unit testing and I am able to test everything within a component but its methods.
I'm using Wrapper and mounting the component (I've used both mount
and shallowMount
ways, both of them works for different purposes but none of them let me access the methods defined inside my component).
I think it's a bug since I'm able to access every element of the component instance through wrapper.vm...
like:
wrapper.vm.$data.myVar
wrapper.vm.$props...
wrapper.vm.$router...
wrapper.vm.$children...
wrapper.vm.$emit...
And I can even do the same with inner components and all of them work properly. However, when I want to use jest.spyOn
to check if the method is called, what returns, etc. I'm not able to do that.
I've tried next things passed to jest.spyOn()
, but none of them works:
- Access it through
const myMethod = jest.spyOn(wrapper.vm.$options.methods, 'myMethod');
and later update the wrapper (to know I'm spying on the method), but it doesn't work - Access it using
const myMethod = jest.spyOn(Component.prototype, 'myMethod');
but it getsundefined
- Use
const myMethod = jest.spyOn(Component.methods, 'myMethod');
but then I'll getProperty 'methods' does not exist on type 'VueConstructor<Vue>'.ts(2339)
- Finally, using
const myMethod = jest.spyOn(Component.prototype.methods, 'myMethod');
and also I getCannot spyOn on a primitive value; undefined given
In summary, accessing it anywhere to pass it to spyOn
methods gets undefined. Also tried pass it as method when mounting the instance like this but I also get an error:
const myMethod = jest.fn()
wrapper = shallowMount(App, {
methods: {
myMethod
}
})
. . . // Perform any actions needed to trigger the method call
expect(myMethod).toHaveBeenCalled()
In the other hand, I've tried both defining it inside the component as a common method inside any TypeScript class, and also to define it inside the options of @Component
decorator of vue-property-decorator
like this:
@Component({
methods: {
myMethod: () => { ... }
}
})
I've only a way of doing it "work" which is using this tricky thing: const myMethod = jest.spyOn(Component.prototype.constructor.options.method, 'myMethod');
, but it's a ugly workaround and it's actually not valid since, although it tests properly my methods, later when I try to build or serve the application it throws an error:
Property 'options' does not exist on type 'Function'.
Steps to reproduce
- Create a new Vue.js project with CLI using TypeScript
- Create any method inside any component (it's ok use the root component)
- Bind the method to some interaction in the component like clicking a button, or even just in some of the hooks like
mounted()
- Try to spy it using
jest.spyOn()
with the method or pass it to themount()
method to test it has been called
I leave also this link to StackOverflow from another person experiencing this problem: https://stackoverflow.com/questions/63001188/test-method-called-in-mounted-hook-vue-class-component-typescript/63052803?noredirect=1#comment111510952_63052803
Expected behaviour
I think it should be some bug when setting/binding the methods existing in the component to the methods option, since it is possible to access any of the options, data, props, etc. using wrapper.vm
and wrapper.vm.$options.methods
does not work.
Said this, the expected behaviour would be being able to access any defined methods through wrapper.vm.$options.methods
as any other property.
Also, having it accessible through Component.prototype
would be ok.
Actual behaviour
The methods are not found and it gets undefined
when tries to spy it or check they were called.
Possible Solution
I think the problem when using jest.spyOn(Component.prototype, 'myMethod');
is just that as the Component extends Vue class, it's being seen as an instance of Vue
and it actually has no method myMethod
. Maybe just setting type of Component exactly to the actual class (child) of the Component would do the work.
In the other hand, I think the problem with mount
is that when mounting the component, the property wrapper.vm.$options.methods
is not being set correctly. It should check any methods inside the body of the component and assign them to that property and/or looking for the methods
property inside the component/decorator and assign them.