testing.md 5.8 KB

Tests

Les parties principales que l'on veut couvrir par des tests unitaires en Vuex sont les mutations et les actions.

Tester les mutations

Les mutations sont très simples à tester, parce que ce sont juste des fonctions qui se passent complètement sur leurs arguments. Une astuce est que si vous utilisez les modules ES2015 et mettez vos mutations dans votre fichier store.js, en plus de l'export par défaut, vous pouvez également vos mutations avec un export nommé :

const state = { ... }

// export mutations as a named export
export const mutations = { ... }

export default new Vuex.Store({
  state,
  mutations
})

Exemple de test de mutation utilisant Mocha + Chai (vous pouvez utiliser n'importe quel framework/bibliothèque d'assertion selon votre préférence) :

// mutations.js
export const mutations = {
  increment: state => state.count++
}
// mutations.spec.js
import { expect } from 'chai'
import { mutations } from './store'

// destructure assign mutations
const { increment } = mutations

describe('mutations', () => {
  it('INCREMENT', () => {
    // mock state
    const state = { count: 0 }
    // apply mutation
    increment(state)
    // assert result
    expect(state.count).to.equal(1)
  })
})

Tester les actions

Les actions sont un peu plus compliquées car elles peuvent faire appel à des APIs externes. Lorsque l'on teste des actions, on a souvent besoin de faire du mocking — par exemple, on peut abstraire l'appel API dans un service et mocker ce service dans nos tests. Afin de mocker facilement les dépendances, on peut utiliser Webpack et inject-loader pour regrouper nos fichiers de test.

Exemple de test d'une action asynchrone :

// actions.js
import shop from '../api/shop'

export const getAllProducts = ({ commit }) => {
  commit('REQUEST_PRODUCTS')
  shop.getProducts(products => {
    commit('RECEIVE_PRODUCTS', products)
  })
}
// actions.spec.js

// use require syntax for inline loaders.
// with inject-loader, this returns a module factory
// that allows us to inject mocked dependencies.
import { expect } from 'chai'
const actionsInjector = require('inject!./actions')

// create the module with our mocks
const actions = actionsInjector({
  '../api/shop': {
    getProducts (cb) {
      setTimeout(() => {
        cb([ /* mocked response */ ])
      }, 100)
    }
  }
})

// helper for testing action with expected mutations
const testAction = (action, args, state, expectedMutations, done) => {
  let count = 0

  // mock commit
  const commit = (type, payload) => {
    const mutation = expectedMutations[count]
    expect(mutation.type).to.equal(type)
    if (payload) {
      expect(mutation.payload).to.deep.equal(payload)
    }
    count++
    if (count >= expectedMutations.length) {
      done()
    }
  }

  // call the action with mocked store and arguments
  action({ commit, state }, ...args)

  // check if no mutations should have been dispatched
  if (expectedMutations.length === 0) {
    expect(count).to.equal(0)
    done()
  }
}

describe('actions', () => {
  it('getAllProducts', done => {
    testAction(actions.getAllProducts, [], {}, [
      { type: 'REQUEST_PRODUCTS' },
      { type: 'RECEIVE_PRODUCTS', payload: { /* mocked response */ } }
    ], done)
  })
})

Tester les getters

Si vos getters font des calculs compliqués, il peut être judicieux de les tester. Les getters sont également très simples à tester, pour les mêmes raisons que les mutations.

Exemple de test d'un getter :

// getters.js
export const getters = {
  filteredProducts (state, { filterCategory }) {
    return state.products.filter(product => {
      return product.category === filterCategory
    })
  }
}
// getters.spec.js
import { expect } from 'chai'
import { getters } from './getters'

describe('getters', () => {
  it('filteredProducts', () => {
    // mock state
    const state = {
      products: [
        { id: 1, title: 'Apple', category: 'fruit' },
        { id: 2, title: 'Orange', category: 'fruit' },
        { id: 3, title: 'Carrot', category: 'vegetable' }
      ]
    }
    // mock getter
    const filterCategory = 'fruit'

    // get the result from the getter
    const result = getters.filteredProducts(state, { filterCategory })

    // assert the result
    expect(result).to.deep.equal([
      { id: 1, title: 'Apple', category: 'fruit' },
      { id: 2, title: 'Orange', category: 'fruit' }
    ])
  })
})

Lancer les tests

Si vos mutations et actions sont écrites comme il se doit, les tests ne devraient pas avoir de dépendance directe sur les APIs navigateur après un mocking préalable. Cela signifie que vous pouvez simplement regrouper les tests avec Webpack et les lancer directement dans Node. De façon alternative, vous pouvez utiliser mocha-loader ou Karma + karma-webpack afin d'effectuer les tests dans des vrais navigateurs.

Lancer dans Node

Créez la configuration webpack suivante (ainsi que le fichier .babelrc qui correspond) :

// webpack.config.js
module.exports = {
  entry: './test.js',
  output: {
    path: __dirname,
    filename: 'test-bundle.js'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: 'babel',
        exclude: /node_modules/
      }
    ]
  }
}

Puis :

webpack
mocha test-bundle.js

Lancer dans un navigateur

  1. Installez mocha-loader
  2. Changez l'option entry de la configuration webpack ci-dessus pour 'mocha!babel!./test.js'.
  3. Démarrez webpack-dev-server en utilisant cette configuration.
  4. Pointez votre navigateur sur localhost:8080/webpack-dev-server/test-bundle.

Lancer dans un navigateur avec Karma + karma-webpack

Consultez la procédure sur la documentation vue-loader.