Vue.js(2.0)单元测试问题

时间:2017-09-09 16:45:49

标签: unit-testing vue.js mocha sinon

我正在尝试使用Mocha和Sinon对vuex动作执行单元测试

这是我的 action.spec.js

import actions from '@/vuex/actions'
import * as types from '@/vuex/mutation_types'

describe('actions.js', () => {
  var server, store, lists, successPut, successPost, successDelete

  successDelete = {'delete': true}
  successPost = {'post': true}
  successPut = {'put': true}

  beforeEach(() => {
    // mock shopping lists
    lists = [{
      id: '1',
      title: 'Groceries'
    }, {
      id: '2',
      title: 'Clothes'
    }]

    // mock store commit and dispatch methods
    store = {
      commit: (method, data) => {},
      dispatch: () => {
        return Promise.resolve()
      },
      state: {
        shoppinglists: lists
      }
    }
    sinon.stub(store, 'commit')

    // mock server
    server = sinon.fakeServer.create()
    server.respondWith('GET', /shoppinglists/, xhr => {
      xhr.respond(200, {'Content-Type': 'application/json'}, JSON.stringify(lists))
    })
    server.respondWith('POST', /shoppinglists/, xhr => {
      xhr.respond(200, {'Content-Type': 'application/json'}, JSON.stringify(successPost))
    })
    server.respondWith('PUT', /shoppinglists/, xhr => {
      xhr.respond(200, {'Content-Type': 'application/json'}, JSON.stringify(successPut))
    })
    server.respondWith('DELETE', /shoppinglists/, xhr => {
      xhr.respond(200, {'Content-Type': 'application/json'}, JSON.stringify(successDelete))
    })
    server.autoRespond = true
  })

  afterEach(() => {
    // restore stubs and server mock
    store.commit.restore()
    server.restore()
  })

  describe('populateShoppingLists', () => {
    it('should call commit method with POPULATE_SHOPPING_LIST string parameter', done => {
      actions.populateShoppingLists(store).then(() => {
        expect(store.commit).to.have.been.calledWith(types.POPULATE_SHOPPING_LISTS, lists)
        done()
      }).catch(done)
    })
  })

  describe('changeTitle', () => {
    it('should call commit method with CHANGE_TITLE string', (done) => {
      let title = 'new title'

      actions.changeTitle(store, {title: title, id: '1'}).then(() => {
        expect(store.commit).to.have.been.calledWith(types.CHANGE_TITLE, {title: title, id: '1'})
        done()
      }).catch(done)
    })
  })

  describe('updateList', () => {
    it('should return successful PUT response', (done) => {
      actions.updateList(store, '1').then((data) => {
        expect(data.data).to.eql(successPut)
        done()
      }).catch(done)
    })
  })

  describe('createShoppingList', () => {
    it('should return successful POST response', (done) => {
      let newList = { title: 'new list', id: '3' }
      actions.createShoppingList(store, newList).then((testResponse) => {
        console.log('testResponse: ', testResponse)
        expect(testResponse.body).to.eql(successPost)
        done()
      }).catch(done)
    })
  })
})

这是我的 action.js

import { CHANGE_TITLE, POPULATE_SHOPPING_LISTS } from './mutation_types'
import api from '../api'
import getters from './getters'

export default {
  populateShoppingLists: ({ commit }) => {
    return api.fetchShoppingLists().then(response => {
      commit(POPULATE_SHOPPING_LISTS, response.data)
    })
  },

  changeTitle: (store, data) => {
    store.commit(CHANGE_TITLE, data)
    return store.dispatch('updateList', data.id)
  },

  updateList: (store, id) => {
    let shoppingList = getters.getListById(store.state, id)
    return api.updateShoppingList(shoppingList)
  },

  createShoppingList: (store, shoppinglist) => {
    return api.addNewShoppingList(shoppinglist).then((actionResponse) => {
      console.log('actionResponse: ', actionResponse)
      store.dispatch('populateShoppingLists')
    })
  },
}

运行我的单元测试,我的 createShoppingList 测试有问题

的console.log

  actions.js
    populateShoppingLists
      ✓ should call commit method with POPULATE_SHOPPING_LIST string parameter
    changeTitle
      ✓ should call commit method with CHANGE_TITLE string
    updateList
      ✓ should return successful PUT response
LOG LOG: 'actionResponse: ', Response{url: 'http://localhost:3000/shoppinglists', ok: true, status: 200, statusText: 'OK', headers: Headers{map: Object{Content-Type: ...}}, body: Object{post: true}, bodyText: '{"post":true}'}
LOG LOG: 'testResponse: ', undefined
    createShoppingList
      ✗ should return successful POST response
        undefined is not an object (evaluating 'testResponse.body')
        webpack:///test/unit/specs/vuex/actions.spec.js:90:28 <- index.js:15508:28
        webpack:///~/vue-resource/dist/vue-resource.es2015.js:151:0 <- index.js:17984:52
        webpack:///~/vue/dist/vue.esm.js:701:0 <- index.js:3198:18
        nextTickHandler@webpack:///~/vue/dist/vue.esm.js:648:0 <- index.js:3145:16

whicj表示在 createShoppingList 操作中,响应不会在返回时发回,所以expect(testResponse.body).to.eql(successPost)不是真的......

在这种情况下我的承诺处理有什么问题?

感谢您的反馈

1 个答案:

答案 0 :(得分:0)

您走在正确的轨道上 - testResponseundefined,因为createShoppingList使用addNewShoppingList.then的返回值解析,这是未指定的,默认为undefined

createShoppingList应该通过addNewShoppingList的回复或来自populateShoppingLists的回复解决吗?如果是前者,请从处理程序返回actionResponse

return api.addNewShoppingList(shoppinglist).then((actionResponse) => {
  store.dispatch('populateShoppingLists')
  return actionResponse
});

作为旁注,因为您正在测试的操作是承诺,您可以在测试中get rid of done直接返回操作:

it('should call commit method with POPULATE_SHOPPING_LIST string parameter', () => {
  // mocha will fail the test if the promise chain rejects or the expectation is not met
  return actions.populateShoppingLists(store).then(() => {
    expect(store.commit).to.have.been.calledWith(types.POPULATE_SHOPPING_LISTS, lists)
  })
})