首页 文章

在从vuex操作返回的promise中测试VueJS方法

提问于
浏览
1

我有一个Vue组件,它使用来自vuex商店的映射操作,它返回一个promise . 当组件调用映射的操作,并且映射的操作被解析时,我正在调用另一个vue方法 vm.$router.push() . 我想断言 push 方法被调用 . 这是我使用vuex和vue-router创建的组件,测试和一些辅助方法 .

Here is my .vue component with some console.logs to track what's going on.

<template>
  <div>
    <button @click="promise" class="click-promise">Promise</button>
  </div>
</template>

<script>
  import { mapActions } from 'vuex'

  export default {
  methods: {
    ...mapActions(['promiseAction']),
    promise(){
      const vm = this
      vm.$router.push('/some/route')
      console.log('before promiseAction')
      console.log(vm.promiseAction())
      return vm.promiseAction().then(function (response) {
        console.log('inside promiseAction')
        vm.$router.push('/some/other/route')
      })
    }
  }
}
</script>

Here is my test. I'm using Mocha, Karma, Chai, and jquery-chia

import Testing from '@/components/Testing'

describe('Testing.vue', () => {
  const mount = componentHelper(Testing)

  it.only('assert something in a promise returned from an action', () => {
    const promise = new Promise(resolve => resolve('success'))
    const promiseAction = stubAction('promiseAction').returns(promise)
    const vm = mount()
    const routerPush = sinon.spy(vm.$router, 'push')

    $('.click-promise').click()
    expect(promiseAction).to.have.been.called
    expect(routerPush).to.have.been.calledWith('/some/route') //this passes

    return vm.$nextTick(() => {
      console.log('inside nextTick')
      expect(routerPush).to.have.been.calledWith('/some/other/routes') //this never happens
    })
  })
})

And here is my helpers file. I'm not sure if this is 100% neccisary, but I wanted to include everything in this post

import Vue from 'vue'
import Vuex from 'vuex'
import VueRouter from 'vue-router'
Vue.use(Vuex)
Vue.use(VueRouter)

let div

beforeEach(() => {
  // create a dom element for the component to mount to
  div = document.createElement('div')
  document.body.appendChild(div)
})

afterEach(() => {
  // clean up the document nodes after each test
  Array.prototype.forEach.call(document.querySelectorAll('body *:not([type="text/javascript"])'), node => {
    node.parentNode.removeChild(node)
  })
})

// stub out a store config object
const storeConfig = {
  actions: {}
}

/**
 * Set up a function that will attach the mock store to the component
 * and mount the component to the test div element
 *
 * Use like this:
 * const mount = componentHelper(YourComponent)
 * do some setup
 * call mount() to instantiate the mocked component
 *
 * @param component
 * @returns {function()}
 */
window.componentHelper = function (component) {
  const router = new VueRouter({})
    return () => {
    // 1. attaches the stubbed store to the component
    component.store = new Vuex.Store(storeConfig)
    component.router = router
    // 2. mounts the component to the dom element
    // 3. returns the vue instance
    return new Vue(component).$mount(div)
  }
}

/**
 * Creates an action to be added to the fake store
 * returns a sinon stub which can be asserted against
 * @param actionName
 */
window.stubAction = (actionName) => {
  // 1. create a stub
  const stub = sinon.stub()
  // 2. create the action function that will be placed in the store and add it to the store
  storeConfig.actions[actionName] = function (context, ...args) {
    // 3. when this action is called it will call the stub
    // return the stub so you can assert against the stubbed return value
    // example: stubAction('fakeAction').returns('xyz')
    return stub(...args)
    }
  // 4. return the stub that was placed in the return of the action for assertions
  return stub
}

When I run this test this is what I get.

LOG LOG: 'before promiseAction'
LOG LOG: Promise{_c: [], _a: undefined, _s: 1, _d: true, _v: 'success', _h: 0, _n: true}

  Testing.vue
    ✓ assert something in a promise returned from an action

PhantomJS 2.1.1 (Linux 0.0.0): Executed 1 of 4 SUCCESS (0.045 secs / 0.018 secs)
TOTAL: 1 SUCCESS


=============================== Coverage summary ===============================
Statements   : 31.58% ( 6/19 )
Branches     : 100% ( 0/0 )
Functions    : 0% ( 0/2 )
Lines        : 31.58% ( 6/19 )
================================================================================
LOG LOG: 'inside nextTick'
ERROR LOG: '[Vue warn]: Error in nextTick: "AssertionError: expected push to have been called with arguments /some/other/routes
/some/route /some/other/routes "

(found in <Root>)'
ERROR LOG: AssertionError{message: 'expected push to have been called with arguments /some/other/routes
/some/route /some/other/routes ', showDiff: false, actual: push, expected: undefined, stack: undefined, line: 210, sourceURL: 'http://localhost:9877/absolute/home/doug/Code/projects/vue-testing-sandbox/node_modules/chai/chai.js?ab7cf506d9d77c111c878b1e10b7f25348630760'}
LOG LOG: 'inside promiseAction'

正如您所看到的那样,测试通过了,但是承诺内部的代码在测试完成之后才会运行 . 我在谈论这一节

return vm.promiseAction().then(function (response) {
    console.log('inside promiseAction')
    vm.$router.push('/some/other/route')
  })

我还注销了promise函数 console.log(vm.promiseAction()) ,你可以看到它是你所期望的 .

我怎样才能让测试等待承诺?我认为 nextTick 可能是答案,但它似乎没有起作用 .

谢谢你的帮助 .

1 回答

  • 1

    单击按钮,没有一种非常好的方法可以做你想做的事 . 而且,我不确定它是否真的值得通过点击按钮进行测试 . 如果Vue的事件处理程序无法正常工作,则会出现更大的问题 .

    相反,我建议你只需调用 promise 方法并在从该方法返回的promise的成功回调中执行测试 .

    //execute the handler
    const test = vm.promise()
    
    // test that the pre-async actions occured
    expect(promiseAction).to.have.been.called
    expect(routerPush).to.have.been.calledWith('/some/route')
    
    // test the post-async action occurred
    return test.then(() => {
      expect(routerPush).to.have.been.calledWith('/some/other/routes')
    })
    

相关问题