2017-08-12 67 views
1

我有一個Vue組件,它使用來自vuex存儲的映射動作,它返回一個promise。當組件調用映射的動作,並且映射的動作被解析時,我調用另一個vue方法vm.$router.push()。我想斷言push方法被調用。這裏是我的組件,測試和一些輔助方法,我使用vuex和vue-router來創建組件。在vuex動作返回的promise中測試VueJS方法

這裏是我的.vue組件,它帶有一些console.logs來跟蹤發生了什麼。

<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> 

這裏是我的測試。我使用的是摩卡,因果報應,柴,和jQuery嘉

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 
    }) 
    }) 
}) 

這裏是我的助手文件。我不知道這是否是100%neccisary,但我想包括在這個崗位

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 
} 

一切,當我運行這個測試,這是我所得到的。

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') 
    }) 

我也退出了諾言功能console.log(vm.promiseAction()),你可以看到它是你所期望的。

我該如何獲得測試等待承諾?我認爲nextTick可能是答案,但它似乎並沒有工作。

感謝您的任何幫助。

回答

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') 
}) 
+0

謝謝!這工作。唯一需要修改的是'test.then()'必須返回到測試。 'return test.then(()=> {// expectation})' 我認爲如果有一種方法來「點擊」按鈕,然後斷言'$ router.push()'發生後會很好承諾解決。這絕對是一個很好的解決方案。 –

+0

@DougSteinberg很高興聽到它:)我會添加回報。 – Bert