如何编写不需要重置状态的单元测试?

时间:2019-05-28 21:06:52

标签: javascript unit-testing vue.js vuex

我正在尝试编写一个使用VueX构建简单计数器应用程序的Vue.js应用程序。 (该应用是使用npx vue-cli init webpack app命令生成的)

enter image description here

我编写了单元测试,但是发现它们要求我在测试开始时手动将状态清零。更具体地说,如果我单独运行decrement测试($ yarn unit --testNamePattern='decrement'),则一切正常。如果我运行所有测试,则不会重置存储,并且我的代码也会失败(先前的测试会使用非零计数器离开存储)。我真的很想编写测试,这样就不必在应用程序代码内进行任何操作来重置。由于商店是一个实现细节,我显然不想直接在测试中重置它,而不得不模拟VueX之类的东西。

我的vue组件如下所示:

<template>
  <div class="countly">
    <div class="app-name item">Countly</div>
    <div class="tally item">Tally:
      <span class="count">{{ count }}</span>
    </div>
    <div class="item">
    <button class="increment" @click="increment">+</button>
    <button class="decrement" @click="decrement">-</button>
    <button class="zero" @click="zero">0</button>
    </div>
  </div>
</template>

<script>
import { store } from '../store/Countly'

export default {
  name: 'Countly',
  computed: {
    count () {
      return store.state.count
    }
  },
  methods: {
    decrement: function () { store.commit('decrement') },
    increment: function () { store.commit('increment') },
    zero: function () { store.commit('zero') }
  }
}
</script>

<style scoped>
.countly {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  height: 100%;
}

.item {
  padding: 0.2em;
}

.tally {
  font-size: 1.15em;
}
button {
  background-color: blue;
  color: white;
  display: block;
  padding: 1em;
  width: 6em;
  margin: 1em;
  border: 0px;
}
</style>

商店看起来像这样:

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)

export const mutations = {
  increment: state => state.count++,
  decrement: state => {
    // console.log( "Before: ", state.count )
    state.count--
    // console.log( "After: ", state.count )
  },
  zero: state => (state.count = 0) // eslint-disable-line no-return-assign
}

export const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations
})

我有一个针对decrement的测试,该测试仅在我单独运行时才有效。

  it('should decrement', () => {
    // Serious hack. These tests don't start the mounted component from a fresh
    // state. If you run this test individually without the zero() call, they
    // work. But, if you run them with all the other tests, they fail because
    // state is retained from a previous state. Trying to mount with different options
    // (like attactToDocument or localVue) does not affect this. So, we need to call
    // zero to make sure things are in a good state first.
    //  console.log('Count is (should be zero when we first mount, right?!?!): ', getCount(vm))
    zero(vm) // Ugly hack
    decrement(vm)
    decrement(vm)
    expect(getCount(vm)).toEqual('-2')
  })

整个测试套件在这里:

// import Vue from 'vue'
import Countly from '@/components/Countly'
import { mount } from '@vue/test-utils'

describe('Countly.vue', () => {
  const increment = vm => vm.find('button.increment').trigger('click')
  const decrement = vm => vm.find('button.decrement').trigger('click')
  const zero = vm => vm.find('button.zero').trigger('click')

  const getCount = vm => {
    const count = vm.find('.count')
    const rv = count.text()
    return rv
  }

  let vm
  beforeEach(() => {
    vm = mount(Countly)
    // , {
    //   attachToDocument: true
    //   // localVue: true
    // })
    // zero() // If we don't zero it each time, it leaves state in the component...
  })
  // Need this because Vue does not correctly remount each time.
  // afterEach(() => {
  //   vm.destroy()
  // })

  it('should render correct contents', () => {
    const result = vm.find('.app-name')
    expect(result.text()).toEqual('Countly')
  })

  it('should start at 0', () => {
    expect(getCount(vm)).toEqual('0')
  })

  it('should increment', () => {
    increment(vm)
    expect(getCount(vm)).toEqual('1')
  })

  it('should decrement', () => {
    // Serious hack. These tests don't start the mounted component from a fresh
    // state. If you run this test individually without the zero() call, they
    // work. But, if you run them with all the other tests, they fail because
    // state is retained from a previous state. Trying to mount with different options
    // (like attactToDocument or localVue) does not affect this. So, we need to call
    // zero to make sure things are in a good state first.
    //  console.log('Count is (should be zero when we first mount, right?!?!): ', getCount(vm))
    // zero(vm) // Ugly hack
    decrement(vm)
    decrement(vm)
    expect(getCount(vm)).toEqual('-2')
  })

  it('should zero', () => {
    increment(vm)
    increment(vm)
    zero(vm)
    expect(getCount(vm)).toEqual('0')
  })
})

我也有商店的单元测试。

import { mutations } from '@/store/Countly'

// destructure assign `mutations`
const { increment, zero, decrement } = mutations

describe('mutations', () => {
  it('increment', () => {
    const state = { count: 0 }
    increment(state)
    expect(state.count).toEqual(1)
  })

  it('decrement', () => {
    const state = { count: 0 }
    decrement(state)
    expect(state.count).toEqual(-1)
  })

  it('zero', () => {
    const state = { count: 5 }
    zero(state)
    expect(state.count).toEqual(0)
  })
})

如果有关系,jest.config.js文件如下所示:

const path = require('path')

module.exports = {
  rootDir: path.resolve(__dirname, '../../'),
  moduleFileExtensions: [
    'js',
    'json',
    'vue'
  ],
  testURL: 'http://example.com',
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1'
  },
  transform: {
    '^.+\\.js$': '<rootDir>/node_modules/babel-jest',
    '.*\\.(vue)$': '<rootDir>/node_modules/vue-jest'
  },
  testPathIgnorePatterns: [
    '<rootDir>/test/e2e'
  ],
  snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
  setupFiles: ['<rootDir>/test/unit/setup'],
  coverageDirectory: '<rootDir>/test/unit/coverage',
  collectCoverageFrom: [
    'src/**/*.{js,vue}',
    '!src/main.js',
    '!**/node_modules/**'
  ]
}

我的package.json看起来像这样:

{
  "name": "app",
  "version": "1.0.0",
  "description": "A Vue.js project",
  "private": true,
  "scripts": {
    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "unit": "jest --config test/unit/jest.conf.js --coverage",
    "unit-bare": "jest --config test/unit/jest.conf.js",
    "e2e": "node test/e2e/runner.js",
    "test": "npm run unit && npm run e2e",
    "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
    "build": "node build/build.js"
  },
  "dependencies": {
    "vue": "^2.5.2",
    "vuex": "^3.1.1"
  },
  "devDependencies": {
    "@vue/test-utils": "^1.0.0-beta.29",
    "autoprefixer": "^7.1.2",
    "babel-core": "^6.22.1",
    "babel-eslint": "^8.2.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.3",
    "babel-jest": "^21.0.2",
    "babel-loader": "^7.1.1",
    "babel-plugin-dynamic-import-node": "^1.2.0",
    "babel-plugin-syntax-jsx": "^6.18.0",
    "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-plugin-transform-vue-jsx": "^3.5.0",
    "babel-preset-env": "^1.3.2",
    "babel-preset-stage-2": "^6.22.0",
    "babel-register": "^6.22.0",
    "chalk": "^2.0.1",
    "chromedriver": "^2.27.2",
    "copy-webpack-plugin": "^4.0.1",
    "cross-spawn": "^5.0.1",
    "css-loader": "^0.28.0",
    "eslint": "^4.15.0",
    "eslint-config-standard": "^10.2.1",
    "eslint-friendly-formatter": "^3.0.0",
    "eslint-loader": "^1.7.1",
    "eslint-plugin-import": "^2.7.0",
    "eslint-plugin-node": "^5.2.0",
    "eslint-plugin-promise": "^3.4.0",
    "eslint-plugin-standard": "^3.0.1",
    "eslint-plugin-vue": "^4.0.0",
    "extract-text-webpack-plugin": "^3.0.0",
    "file-loader": "^1.1.4",
    "friendly-errors-webpack-plugin": "^1.6.1",
    "html-webpack-plugin": "^2.30.1",
    "jest": "^22.0.4",
    "jest-serializer-vue": "^0.3.0",
    "nightwatch": "^0.9.12",
    "node-notifier": "^5.1.2",
    "optimize-css-assets-webpack-plugin": "^3.2.0",
    "ora": "^1.2.0",
    "portfinder": "^1.0.13",
    "postcss-import": "^11.0.0",
    "postcss-loader": "^2.0.8",
    "postcss-url": "^7.2.1",
    "rimraf": "^2.6.0",
    "selenium-server": "^3.0.1",
    "semver": "^5.3.0",
    "shelljs": "^0.7.6",
    "uglifyjs-webpack-plugin": "^1.1.1",
    "url-loader": "^0.5.8",
    "vue-jest": "^1.0.2",
    "vue-loader": "^13.3.0",
    "vue-style-loader": "^3.0.1",
    "vue-template-compiler": "^2.5.2",
    "webpack": "^3.6.0",
    "webpack-bundle-analyzer": "^2.9.0",
    "webpack-dev-server": "^2.9.1",
    "webpack-merge": "^4.1.0"
  },
  "engines": {
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}

0 个答案:

没有答案