4

How can I stub certain methods (getters, in particular) from Vue single file components for unit testing with mocha/expect?

The problem I was facing was the following: I have a component with a get method someData

<script lang="ts">
import { Vue, Component } from 'vue-property-decorator'
import SomeService from '@/services/some.service'

@Component()
export default class MyApp extends Vue {
    ...

    mounted () {
      ...
    }

    get someData () {
      return this.$route.path.split('/')[1] || 'main'
    }

    get getLocation () {
      return this.someService.getBaseURL()
    }

    initSomeStringProperty (): string {
      return 'some string'
    }
}
</script>

My tests always fail with:

[Vue warn]: Error in render: "TypeError: Cannot read property 'path' of undefined"

When I try to stub the method using sinon, like following:

describe('MyApp.vue', () => {
  if('returns main', () => {
    const dataStub = sinon.stub(MyApp, 'someData')
    listStub.yields(undefined, 'main')
    const wrapper = shallowMount(AppNavBar)
    expect(wrapper.text()).to.include('Some Content')
  })
})

However, I get the following error:

TypeError: Cannot stub non-existent own property someData

In addition, I get the same error for every other method, I want to stub analogously, e.g., initSomeStringProperty().

nymvno
  • 370
  • 5
  • 19

2 Answers2

1

In the code above someData is computed property that is defined with property accessor through vue-property-decorator.

It can be stubbed at two points, either on class prototype:

sinon.stub(MyApp.prototype, 'someData').get(() => 'foo');

Or component options:

sinon.stub(MyApp.options.computed.someData, 'get').value('foo');
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
1

You could set the component's computed props and methods upon mounting, as shown below. Update: As of 1.x, setting methods has been deprecated in favor of stubbing (see @EstusFlask's answer on how to properly stub with Sinon).

const wrapper = shallowMount(MyApp, {
  computed: {
    someData: () => 'foo'
  },
  methods: {
    initSomeStringProperty: () => 'bar'
  }
})
expect(wrapper.vm.someData).to.equal('foo')
expect(wrapper.vm.initSomeStringProperty()).to.equal('bar')

If you were just trying to avoid the error about $route being undefined, you could mock $route upon mounting:

const wrapper = shallowMount(MyApp, {
  mocks: {
    $route: { path: '/home' }
  }
})
expect(wrapper.vm.someData).to.equal('home')
tony19
  • 125,647
  • 18
  • 229
  • 307
  • 1
    "overwriting methods via the `methods` property is deprecated and will be removed in the next major version. There is no clear migration path for the `methods` property - Vue does not support arbitrarily replacement of methods, nor should VTU. To stub a complex method extract it from the component and test it in isolation. Otherwise, the suggestion is to rethink those tests." – Vongarnichts undStuff Aug 10 '20 at 09:36
  • 1
    @VongarnichtsundStuff Added note about deprecation – tony19 Aug 10 '20 at 18:55