2

In this test, I am simply trying to assert that the end property is set on the component under test (DateRangeEditor). The component assumes this.$refs.datepicker is an instance of HotelDatepicker and calls its setCheckOut and setCheckIn methods (if there is a better way to do this please let me know!).

The component works when loaded normally in the browser, but my test for setEndDate is failing with "TypeError: this.$refs.datepicker.setCheckIn is not a function".

Am I misunderstanding what setting the stubs option is supposed to do? I thought scenarios like this, where the calling code just needs to call the stubbed function and doesn't need a specific result, were exactly what stubs are for. Am I just using it wrong?

Here's my test:

import { mount, shallow } from 'vue-test-utils'
import DateRangeEditor from '../../assets/src/components/DateRangeEditor.vue'

describe('DateRangeEditor', () => {

  describe('@checkOutChanged', () => {
    it('sets a new end date', () => {
      const wrapper = shallow(DateRangeEditor, {
        stubs: ['datepicker']
      })

      // simulate user changing the end date
      const date = new Date(2017, 11, 1)
      wrapper.vm.$refs.datepicker.$emit('checkOutChanged', date)

      expect(wrapper.vm.end).to.equal(date)
    })
  })

  describe('@checkInChanged', () => {
    it('sets a new start date', () => {
      const wrapper = shallow(DateRangeEditor, {
        stubs: ['datepicker']
      })

      // simulate user changing the start date
      const date = new Date(2017, 11, 1)
      wrapper.vm.$refs.datepicker.$emit('checkInChanged', date)

      expect(wrapper.vm.start).to.equal(date)
    })
  })
})

And here is my (greatly simplified) DateRangeEditor component:

<template>
  <div class="date-range-editor-container">
    <datepicker
      @checkInChanged="setStartDate"
      @checkOutChanged="setEndDate"
      ref="datepicker"
    ></datepicker>
    <button type="button" @click="save" class="btn save-btn">Save</button>
    <button type="button" @click="cancel" class="btn cancel-btn">Cancel</button>
  </div>
</template>

<script>
import HotelDatePicker from 'vue-hotel-datepicker'

export default {
  methods: {
    setStartDate: function(newDate) {
      if (newDate) {
        // make sure newDate has an hour component
        newDate.setHours((new Date()).getHours())
        this.$refs.datepicker.setCheckIn(newDate)
        this.start = newDate
      }
    },
    setEndDate: function(newDate) {
      if (newDate) {
        // make sure newDate has an hour component
        newDate.setHours((new Date()).getHours())
        this.$refs.datepicker.setCheckOut(newDate)
        this.end = newDate
      }
    },
    save: function() {
      this.$emit('save', this.$data)
    },
    cancel: function() {
      this.$emit('cancel', this.$data)
    },
  },
  data: function() {
    return {
      start: '',
      end: '',
    }
  },
  components: {
    datepicker: HotelDatePicker,
  }
}
</script>

<style>
</style>

Finally, here's my setup.js bootstrap file:

/* globals global */
require('jsdom-global')()
const chai    = require('chai')
const sinon   = require('sinon')

// use Sinon test spies/stubs/mocks in all tests
chai.use(require('chai-sinon'))

// expose libs to all tests
global.chai     = chai
global.sinon    = sinon
global.expect   = chai.expect

versions n stuff

  • @vue/test-utils ^1.0.0-beta.10
  • vue ^2.4.4

1 Answer 1

5

stubs replaces child component methods. This is why you're getting the error.

If you want to check that a method is being called, you can create a component with the methods and pass it in the stubs option.

const datepicker = {
    setCheckIn: () => {},
    setCheckOutDate: () => {},
    render: () => {}
}

it('sets a new end date', () => {
  const wrapper = shallow(DateRangeEditor, {
    stubs: {
      datepicker
    }
  })

  const date = new Date(2017, 11, 1)
  wrapper.vm.$refs.datepicker.$emit('checkOutChanged', date)
  expect(wrapper.vm.end).to.equal(date)
})
Sign up to request clarification or add additional context in comments.

3 Comments

Hi @Edd, thanks for your answer and for your work on Vue! Yeah, I tried that initially, but when I do that, I get "Error: [vue-test-utils]: options.stub values must be passed a string or component". And if I pass mocks instead, (everything else the same, essentially s/stubs:/mocks:/), I get the same TypeError as before.
I've updated my example to include a render function on the stub. At the moment, vue-test-utils needs a render function for the stub to valid (we'll fix this as it's not always the case). Try again with the example I added, it should work as expected 🙂
That fixed it. Writing up a GH issue for that now, thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.