Skip to content

Accessing component methods for unit testing using typescript is not working #1623

@t0nyak

Description

@t0nyak

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 gets undefined
  • Use const myMethod = jest.spyOn(Component.methods, 'myMethod'); but then I'll get Property 'methods' does not exist on type 'VueConstructor<Vue>'.ts(2339)
  • Finally, using const myMethod = jest.spyOn(Component.prototype.methods, 'myMethod'); and also I get Cannot 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

  1. Create a new Vue.js project with CLI using TypeScript
  2. Create any method inside any component (it's ok use the root component)
  3. Bind the method to some interaction in the component like clicking a button, or even just in some of the hooks like mounted()
  4. Try to spy it using jest.spyOn() with the method or pass it to the mount() 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions