A little helper to unit test Vue components in the Cypress.io E2E test runner

cypress-vue-unit-test

A little helper to unit test Vue components in the open source Cypress.io E2E test runner v4.5.0+

Jump to: Comparison, Blog posts, Examples: basic, advanced, full, external, Code coverage

TLDR

  • What is this? This package allows you to use Cypress test runner to unit test your Vue components with zero effort.

Example component test

  • How is this different from vue-test-utils? Vue Test Utils uses Node, requires stubbing browser APIs, and requires users to await Vue’s internal event loop. Cypress Vue Unit Test runs each component in the real browser with full power of Cypress E2E test runner: live GUI, full API, screen recording, CI support, cross-platform.

  • If you like using @testing-library/vue, you can use @testing-library/cypress for the same findBy, queryBy commands, see one of the examples in the list below

Blog posts

Install

Terminal typing vue add cypress-experimental

Vue CLI Installation

Vue CLI v3+

Recommended: One step install to existing projects with Vue CLI via experimental plugin, read Write Your First Vue Component Test

vue add cypress-experimental

If you want to install this package manually, follow manual install

Usage and Examples

// components/HelloWorld.spec.js
import { mount } from 'cypress-vue-unit-test'
import { HelloWorld } from './HelloWorld.vue'
describe('HelloWorld component', () => {
  it('works', () => {
    mount(HelloWorld)
    // now use standard Cypress commands
    cy.contains('Hello World!').should('be.visible')
  })
})

Options

You can pass additional styles, css files and external stylesheets to load, see docs/styles.md for full list.

import Todo from './Todo.vue'
const todo = {
  id: '123',
  title: 'Write more tests',
}

mount(Todo, {
  propsData: { todo },
  stylesheets: [
    'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css',
  ],
})

See examples below for details.

Global Vue Options

You can pass extensions (global components, mixins, modules to use) when mounting Vue component. Use { extensions: { ... }} object inside the options.

  • components - object of ‘id’ and components to register globally, see Components example
  • use (alias plugins) - list of plugins, see Plugins
  • mixin (alias mixins) - list of global mixins, see Mixins example
  • filters - hash of global filters, see Filters example

The intro example

Take a look at the first Vue v2 example: Declarative Rendering. The code is pretty simple

<div id="app">
  {{ message }}
</div>
var app = new Vue({
  el: '#app',
  data() {
    return { message: 'Hello Vue!' }
  },
})

It shows the message when running in the browser

Hello Vue!

Let’s test it in Cypress.io (for the current version see cypress/integration/spec.js).

import { mountCallback } from 'cypress-vue-unit-test'

describe('Declarative rendering', () => {
  // Vue code from https://vuejs.org/v2/guide/#Declarative-Rendering
  const template = `
    <div id="app">
      {{ message }}
    </div>
  `

  const data = {
    message: 'Hello Vue!',
  }

  // that's all you need to do
  beforeEach(mountCallback({ template, data }))

  it('shows hello', () => {
    cy.contains('Hello Vue!')
  })

  it('changes message if data changes', () => {
    // mounted Vue instance is available under Cypress.vue
    Cypress.vue.message = 'Vue rocks!'
    cy.contains('Vue rocks!')
  })
})

Fire up Cypress test runner and have real browser (Electron, Chrome) load Vue and mount your test code and be able to interact with the instance through the reference Cypress.vue.$data and via GUI. The full power of the Cypress API is available.

Hello world tested

The list example

There is a list example next in the Vue docs.

<div id="app-4">
  <ol>
    <li v-for="todo in todos">
      {{ todo.text }}
    </li>
  </ol>
</div>
var app4 = new Vue({
  el: '#app-4',
  data: {
    todos: [
      { text: 'Learn JavaScript' },
      { text: 'Learn Vue' },
      { text: 'Build something awesome' },
    ],
  },
})

Let’s test it. Simple.

import { mountCallback } from 'cypress-vue-unit-test'

describe('Declarative rendering', () => {
  // List example from https://vuejs.org/v2/guide/#Declarative-Rendering
  const template = `
    <ol>
      <li v-for="todo in todos">
        {{ todo.text }}
      </li>
    </ol>
  `

  function data() {
    return {
      todos: [
        { text: 'Learn JavaScript' },
        { text: 'Learn Vue' },
        { text: 'Build something awesome' },
      ],
    }
  }

  beforeEach(mountCallback({ template, data }))

  it('shows 3 items', () => {
    cy.get('li').should('have.length', 3)
  })

  it('can add an item', () => {
    Cypress.vue.todos.push({ text: 'Test using Cypress' })
    cy.get('li').should('have.length', 4)
  })
})

List tested

Handling User Input

The next section in the Vue docs starts with reverse message example.

<div id="app-5">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">Reverse Message</button>
</div>
var app5 = new Vue({
  el: '#app-5',
  data: {
    message: 'Hello Vue.js!',
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    },
  },
})

We can write the test the same way

import { mountCallback } from 'cypress-vue-unit-test'

describe('Handling User Input', () => {
  // Example from https://vuejs.org/v2/guide/#Handling-User-Input
  const template = `
    <div>
      <p>{{ message }}</p>
      <button v-on:click="reverseMessage">Reverse Message</button>
    </div>
  `

  function data() {
    return { message: 'Hello Vue.js!' }
  }

  const methods = {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    },
  }

  beforeEach(mountCallback({ template, data, methods }))

  it('reverses text', () => {
    cy.contains('Hello Vue')
    cy.get('button').click()
    cy.contains('!sj.euV olleH')
  })
})

Take a look at the video of the test. When you hover over the CLICK step the test runner is showing before and after DOM snapshots. Not only that, the application is fully functioning, you can interact with the application because it is really running!

Reverse input

Component example

Let us test a complex example. Let us test a single file Vue component. Here is the Hello.vue file

<template>
  <p>{{ greeting }} World!</p>
</template>

<script>
export default {
  data() {
    return {
      greeting: 'Hello',
    }
  },
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

note to learn how to load Vue component files in Cypress, see Bundling section.

Do you want to interact with the component? Go ahead! Do you want to have multiple components? No problem!

import Hello from '../../components/Hello.vue'
import { mountCallback } from 'cypress-vue-unit-test'
describe('Several components', () => {
  const template = `
    <div>
      <hello></hello>
      <hello></hello>
      <hello></hello>
    </div>
  `
  const components = {
    hello: Hello,
  }
  beforeEach(mountCallback({ template, components }))

  it('greets the world 3 times', () => {
    cy.get('p').should('have.length', 3)
  })
})

Spying example

Button counter component is used in several Vue doc examples

<template>
  <button v-on:click="incrementCounter">{{ counter }}</button>
</template>

<script>
export default {
  data() {
    return {
      counter: 0,
    }
  },

  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    },
  },
}
</script>

<style scoped>
button {
  margin: 5px 10px;
  padding: 5px 10px;
  border-radius: 3px;
}
</style>

Let us test it - how do we ensure the event is emitted when the button is clicked? Simple - let us spy on the event, spying and stubbing is built into Cypress

import ButtonCounter from '../../components/ButtonCounter.vue'
import { mountCallback } from 'cypress-vue-unit-test'

describe('ButtonCounter', () => {
  beforeEach(mountCallback(ButtonCounter))

  it('starts with zero', () => {
    cy.contains('button', '0')
  })

  it('increments the counter on click', () => {
    cy.get('button').click().click().click().contains('3')
  })

  it('emits "increment" event on click', () => {
    const spy = cy.spy()
    Cypress.vue.$on('increment', spy)
    cy.get('button')
      .click()
      .click()
      .then(() => {
        expect(spy).to.be.calledTwice
      })
  })
})

The component is really updating the counter in response to the click and is emitting an event.

Spying test

XHR spying and stubbing

The mount function automatically wraps XMLHttpRequest giving you an ability to intercept XHR requests your component might do. For full documentation see Network Requests. In this repo see components/AjaxList.vue and the corresponding tests cypress/integration/ajax-list-spec.js.

// component use axios to get list of users
created() {
  axios.get(`https://jsonplaceholder.cypress.io/users?_limit=3`)
  .then(response => {
    // JSON responses are automatically parsed.
    this.users = response.data
  })
}
// test can observe, return mock data, delay and a lot more
beforeEach(mountCallback(AjaxList))
it('can inspect real data in XHR', () => {
  cy.server()
  cy.route('/users?_limit=3').as('users')
  cy.wait('@users').its('response.body').should('have.length', 3)
})
it('can display mock XHR response', () => {
  cy.server()
  const users = [{id: 1, name: 'foo'}]
  cy.route('GET', '/users?_limit=3', users).as('users')
  cy.get('li').should('have.length', 1)
    .first().contains('foo')
})

Spying on window.alert

Calls to window.alert are automatically recorded, but do not show up. Instead you can spy on them, see AlertMessage.vue and its test cypress/integration/alert-spec.js

Comparison

Feature Vue Test Utils or @testing-library/vue Cypress + cypress-vue-unit-test
Test runs in real browser
Uses full mount
Test speed 🏎 as fast as the app works in the browser
Test can use additional plugins maybe use any Cypress plugin
Test can interact with component synthetic limited API use any Cypress command
Test can be debugged via terminal and Node debugger use browser DevTools
Built-in time traveling debugger Cypress time traveling debugger
Re-run tests on file or test change
Test output on CI terminal terminal, screenshots, videos
Tests can be run in parallel ✅ via parallelization
Test against interface if using @testing-library/vue ✅ and can use @testing-library/cypress
Spying and mocking Jest mocks Sinon library
Code coverage

Examples

// components/HelloWorld.spec.js
import { mount } from 'cypress-vue-unit-test'
import { HelloWorld } from './HelloWorld.vue'
describe('HelloWorld component', () => {
  it('works', () => {
    mount(HelloWorld)
    // now use standard Cypress commands
    cy.contains('Hello World!').should('be.visible')
  })
})

Basic examples

Spec Description
Components Registers global components to use
Filters Registering global filters
Hello Testing examples from Vue2 cookbook
Mixins Registering Vue mixins
Plugins Loading additional plugins
Props Pass props to the component during mount
Slots Passing slots and scopedSlots to the component
Small examples A few small examples testing forms, buttons

Advanced examples

Spec Description
access-component Access the mounted component directly from test
i18n Testing component that uses Vue I18n plugin
mocking-axios Mocking 3rd party CommonJS modules like axios
mocking-components Mocking locally registered child components during tests
mocking-imports Stub ES6 imports from the tests
render-functions Mounting components with a render function

Full examples

We have several subfolders in examples folder.

Folder Name Description
cli An example app scaffolded using Vue CLI and the component testing added using vue add cypress-experimental command.

External examples

Repo Description
vue-component-test-example Scaffolded Vue CLI v3 project with added component tests, read Write Your First Vue Component Test.

Known problems

Bundling

How do we load this Vue file into the testing code? Using webpack preprocessor. Note that this module ships with @cypress/webpack-preprocessor 2.x that requires Webpack 4.x. If you have Webpack 3.x please add @cypress/webpack-preprocessor v1.x.

Short way

For Webpack Users

Your project probably already has webpack.config.js setup to transpile .vue files. To load these files in the Cypress tests, grab the webpack processor included in this module, and load it from the cypress/plugins/index.js file.

const {
  onFilePreprocessor,
} = require('cypress-vue-unit-test/preprocessor/webpack')
module.exports = (on) => {
  on('file:preprocessor', onFilePreprocessor('../path/to/webpack.config'))
}

Cypress should be able to import .vue files in the tests

Manual

Using @cypress/webpack-preprocessor and vue-loader. You can use cypress/plugins/index.js to load .vue files using vue-loader.

// cypress/plugins/index.js
const webpack = require('@cypress/webpack-preprocessor')
const webpackOptions = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
    ],
  },
}

const options = {
  // send in the options from your webpack.config.js, so it works the same
  // as your app's code
  webpackOptions,
  watchOptions: {},
}

module.exports = (on) => {
  on('file:preprocessor', webpack(options))
}

Install dev dependencies

npm i -D @cypress/webpack-preprocessor \
  vue-loader vue-template-compiler css-loader

And write a test

import Hello from '../../components/Hello.vue'
import { mountCallback } from 'cypress-vue-unit-test'

describe('Hello.vue', () => {
  beforeEach(mountCallback(Hello))

  it('shows hello', () => {
    cy.contains('Hello World!')
  })
})

Code coverage

This plugin uses babel-plugin-istanbul to automatically instrument .js and .vue files and generates the code coverage report using dependency cypress-io/code-coverage (included). The output reports are saved in the folder “coverage” at the end of the test run.

If you want to disable code coverage instrumentation and reporting, use --env coverage=false or CYPRESS_coverage=false or set in your cypress.json file

{
  "env": {
    "coverage": false
  }
}

Development

To see all local tests, install dependencies, build the code and open Cypress in GUI mode

npm install
npm run build
npm run cy:open

The build is done using tsc that transpiles all files from src to dist folder.

Debugging

Run Cypress with environment variable

DEBUG=cypress-vue-unit-test

If some deeply nested objects are abbreviated and do not print fully, set the maximum logging depth

DEBUG=cypress-vue-unit-test DEBUG_DEPTH=10

FAQ

  • If your component’s static assets are not loading, you probably need to start and proxy Webpack dev server. See issue #4

Related info

Migration guide

From v2 to v3

  • update cypress/plugins/index.js file to pass the on, config arguments when creating the default preprocessor. See change, in general the new way is:
const {
  onFileDefaultPreprocessor,
} = require('cypress-vue-unit-test/preprocessor/webpack')

module.exports = (on, config) => {
  require('@cypress/code-coverage/task')(on, config)
  on('file:preprocessor', onFileDefaultPreprocessor(config))

  // IMPORTANT to return the config object
  // with the any changed environment variables
  return config
}

Test adapters for other frameworks

Contributors

Download Details:

Author: bahmutov

GitHub: https://github.com/bahmutov/cypress-vue-unit-test

#vuejs #vue #javascript #vue-js

What is GEEK

Buddha Community

A little helper to unit test Vue components in the Cypress.io E2E test runner

A little helper to unit test Vue components in the Cypress.io E2E test runner

cypress-vue-unit-test

A little helper to unit test Vue components in the open source Cypress.io E2E test runner v4.5.0+

Jump to: Comparison, Blog posts, Examples: basic, advanced, full, external, Code coverage

TLDR

  • What is this? This package allows you to use Cypress test runner to unit test your Vue components with zero effort.

Example component test

  • How is this different from vue-test-utils? Vue Test Utils uses Node, requires stubbing browser APIs, and requires users to await Vue’s internal event loop. Cypress Vue Unit Test runs each component in the real browser with full power of Cypress E2E test runner: live GUI, full API, screen recording, CI support, cross-platform.

  • If you like using @testing-library/vue, you can use @testing-library/cypress for the same findBy, queryBy commands, see one of the examples in the list below

Blog posts

Install

Terminal typing vue add cypress-experimental

Vue CLI Installation

Vue CLI v3+

Recommended: One step install to existing projects with Vue CLI via experimental plugin, read Write Your First Vue Component Test

vue add cypress-experimental

If you want to install this package manually, follow manual install

Usage and Examples

// components/HelloWorld.spec.js
import { mount } from 'cypress-vue-unit-test'
import { HelloWorld } from './HelloWorld.vue'
describe('HelloWorld component', () => {
  it('works', () => {
    mount(HelloWorld)
    // now use standard Cypress commands
    cy.contains('Hello World!').should('be.visible')
  })
})

Options

You can pass additional styles, css files and external stylesheets to load, see docs/styles.md for full list.

import Todo from './Todo.vue'
const todo = {
  id: '123',
  title: 'Write more tests',
}

mount(Todo, {
  propsData: { todo },
  stylesheets: [
    'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css',
  ],
})

See examples below for details.

Global Vue Options

You can pass extensions (global components, mixins, modules to use) when mounting Vue component. Use { extensions: { ... }} object inside the options.

  • components - object of ‘id’ and components to register globally, see Components example
  • use (alias plugins) - list of plugins, see Plugins
  • mixin (alias mixins) - list of global mixins, see Mixins example
  • filters - hash of global filters, see Filters example

The intro example

Take a look at the first Vue v2 example: Declarative Rendering. The code is pretty simple

<div id="app">
  {{ message }}
</div>
var app = new Vue({
  el: '#app',
  data() {
    return { message: 'Hello Vue!' }
  },
})

It shows the message when running in the browser

Hello Vue!

Let’s test it in Cypress.io (for the current version see cypress/integration/spec.js).

import { mountCallback } from 'cypress-vue-unit-test'

describe('Declarative rendering', () => {
  // Vue code from https://vuejs.org/v2/guide/#Declarative-Rendering
  const template = `
    <div id="app">
      {{ message }}
    </div>
  `

  const data = {
    message: 'Hello Vue!',
  }

  // that's all you need to do
  beforeEach(mountCallback({ template, data }))

  it('shows hello', () => {
    cy.contains('Hello Vue!')
  })

  it('changes message if data changes', () => {
    // mounted Vue instance is available under Cypress.vue
    Cypress.vue.message = 'Vue rocks!'
    cy.contains('Vue rocks!')
  })
})

Fire up Cypress test runner and have real browser (Electron, Chrome) load Vue and mount your test code and be able to interact with the instance through the reference Cypress.vue.$data and via GUI. The full power of the Cypress API is available.

Hello world tested

The list example

There is a list example next in the Vue docs.

<div id="app-4">
  <ol>
    <li v-for="todo in todos">
      {{ todo.text }}
    </li>
  </ol>
</div>
var app4 = new Vue({
  el: '#app-4',
  data: {
    todos: [
      { text: 'Learn JavaScript' },
      { text: 'Learn Vue' },
      { text: 'Build something awesome' },
    ],
  },
})

Let’s test it. Simple.

import { mountCallback } from 'cypress-vue-unit-test'

describe('Declarative rendering', () => {
  // List example from https://vuejs.org/v2/guide/#Declarative-Rendering
  const template = `
    <ol>
      <li v-for="todo in todos">
        {{ todo.text }}
      </li>
    </ol>
  `

  function data() {
    return {
      todos: [
        { text: 'Learn JavaScript' },
        { text: 'Learn Vue' },
        { text: 'Build something awesome' },
      ],
    }
  }

  beforeEach(mountCallback({ template, data }))

  it('shows 3 items', () => {
    cy.get('li').should('have.length', 3)
  })

  it('can add an item', () => {
    Cypress.vue.todos.push({ text: 'Test using Cypress' })
    cy.get('li').should('have.length', 4)
  })
})

List tested

Handling User Input

The next section in the Vue docs starts with reverse message example.

<div id="app-5">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">Reverse Message</button>
</div>
var app5 = new Vue({
  el: '#app-5',
  data: {
    message: 'Hello Vue.js!',
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    },
  },
})

We can write the test the same way

import { mountCallback } from 'cypress-vue-unit-test'

describe('Handling User Input', () => {
  // Example from https://vuejs.org/v2/guide/#Handling-User-Input
  const template = `
    <div>
      <p>{{ message }}</p>
      <button v-on:click="reverseMessage">Reverse Message</button>
    </div>
  `

  function data() {
    return { message: 'Hello Vue.js!' }
  }

  const methods = {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    },
  }

  beforeEach(mountCallback({ template, data, methods }))

  it('reverses text', () => {
    cy.contains('Hello Vue')
    cy.get('button').click()
    cy.contains('!sj.euV olleH')
  })
})

Take a look at the video of the test. When you hover over the CLICK step the test runner is showing before and after DOM snapshots. Not only that, the application is fully functioning, you can interact with the application because it is really running!

Reverse input

Component example

Let us test a complex example. Let us test a single file Vue component. Here is the Hello.vue file

<template>
  <p>{{ greeting }} World!</p>
</template>

<script>
export default {
  data() {
    return {
      greeting: 'Hello',
    }
  },
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

note to learn how to load Vue component files in Cypress, see Bundling section.

Do you want to interact with the component? Go ahead! Do you want to have multiple components? No problem!

import Hello from '../../components/Hello.vue'
import { mountCallback } from 'cypress-vue-unit-test'
describe('Several components', () => {
  const template = `
    <div>
      <hello></hello>
      <hello></hello>
      <hello></hello>
    </div>
  `
  const components = {
    hello: Hello,
  }
  beforeEach(mountCallback({ template, components }))

  it('greets the world 3 times', () => {
    cy.get('p').should('have.length', 3)
  })
})

Spying example

Button counter component is used in several Vue doc examples

<template>
  <button v-on:click="incrementCounter">{{ counter }}</button>
</template>

<script>
export default {
  data() {
    return {
      counter: 0,
    }
  },

  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    },
  },
}
</script>

<style scoped>
button {
  margin: 5px 10px;
  padding: 5px 10px;
  border-radius: 3px;
}
</style>

Let us test it - how do we ensure the event is emitted when the button is clicked? Simple - let us spy on the event, spying and stubbing is built into Cypress

import ButtonCounter from '../../components/ButtonCounter.vue'
import { mountCallback } from 'cypress-vue-unit-test'

describe('ButtonCounter', () => {
  beforeEach(mountCallback(ButtonCounter))

  it('starts with zero', () => {
    cy.contains('button', '0')
  })

  it('increments the counter on click', () => {
    cy.get('button').click().click().click().contains('3')
  })

  it('emits "increment" event on click', () => {
    const spy = cy.spy()
    Cypress.vue.$on('increment', spy)
    cy.get('button')
      .click()
      .click()
      .then(() => {
        expect(spy).to.be.calledTwice
      })
  })
})

The component is really updating the counter in response to the click and is emitting an event.

Spying test

XHR spying and stubbing

The mount function automatically wraps XMLHttpRequest giving you an ability to intercept XHR requests your component might do. For full documentation see Network Requests. In this repo see components/AjaxList.vue and the corresponding tests cypress/integration/ajax-list-spec.js.

// component use axios to get list of users
created() {
  axios.get(`https://jsonplaceholder.cypress.io/users?_limit=3`)
  .then(response => {
    // JSON responses are automatically parsed.
    this.users = response.data
  })
}
// test can observe, return mock data, delay and a lot more
beforeEach(mountCallback(AjaxList))
it('can inspect real data in XHR', () => {
  cy.server()
  cy.route('/users?_limit=3').as('users')
  cy.wait('@users').its('response.body').should('have.length', 3)
})
it('can display mock XHR response', () => {
  cy.server()
  const users = [{id: 1, name: 'foo'}]
  cy.route('GET', '/users?_limit=3', users).as('users')
  cy.get('li').should('have.length', 1)
    .first().contains('foo')
})

Spying on window.alert

Calls to window.alert are automatically recorded, but do not show up. Instead you can spy on them, see AlertMessage.vue and its test cypress/integration/alert-spec.js

Comparison

Feature Vue Test Utils or @testing-library/vue Cypress + cypress-vue-unit-test
Test runs in real browser
Uses full mount
Test speed 🏎 as fast as the app works in the browser
Test can use additional plugins maybe use any Cypress plugin
Test can interact with component synthetic limited API use any Cypress command
Test can be debugged via terminal and Node debugger use browser DevTools
Built-in time traveling debugger Cypress time traveling debugger
Re-run tests on file or test change
Test output on CI terminal terminal, screenshots, videos
Tests can be run in parallel ✅ via parallelization
Test against interface if using @testing-library/vue ✅ and can use @testing-library/cypress
Spying and mocking Jest mocks Sinon library
Code coverage

Examples

// components/HelloWorld.spec.js
import { mount } from 'cypress-vue-unit-test'
import { HelloWorld } from './HelloWorld.vue'
describe('HelloWorld component', () => {
  it('works', () => {
    mount(HelloWorld)
    // now use standard Cypress commands
    cy.contains('Hello World!').should('be.visible')
  })
})

Basic examples

Spec Description
Components Registers global components to use
Filters Registering global filters
Hello Testing examples from Vue2 cookbook
Mixins Registering Vue mixins
Plugins Loading additional plugins
Props Pass props to the component during mount
Slots Passing slots and scopedSlots to the component
Small examples A few small examples testing forms, buttons

Advanced examples

Spec Description
access-component Access the mounted component directly from test
i18n Testing component that uses Vue I18n plugin
mocking-axios Mocking 3rd party CommonJS modules like axios
mocking-components Mocking locally registered child components during tests
mocking-imports Stub ES6 imports from the tests
render-functions Mounting components with a render function

Full examples

We have several subfolders in examples folder.

Folder Name Description
cli An example app scaffolded using Vue CLI and the component testing added using vue add cypress-experimental command.

External examples

Repo Description
vue-component-test-example Scaffolded Vue CLI v3 project with added component tests, read Write Your First Vue Component Test.

Known problems

Bundling

How do we load this Vue file into the testing code? Using webpack preprocessor. Note that this module ships with @cypress/webpack-preprocessor 2.x that requires Webpack 4.x. If you have Webpack 3.x please add @cypress/webpack-preprocessor v1.x.

Short way

For Webpack Users

Your project probably already has webpack.config.js setup to transpile .vue files. To load these files in the Cypress tests, grab the webpack processor included in this module, and load it from the cypress/plugins/index.js file.

const {
  onFilePreprocessor,
} = require('cypress-vue-unit-test/preprocessor/webpack')
module.exports = (on) => {
  on('file:preprocessor', onFilePreprocessor('../path/to/webpack.config'))
}

Cypress should be able to import .vue files in the tests

Manual

Using @cypress/webpack-preprocessor and vue-loader. You can use cypress/plugins/index.js to load .vue files using vue-loader.

// cypress/plugins/index.js
const webpack = require('@cypress/webpack-preprocessor')
const webpackOptions = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
    ],
  },
}

const options = {
  // send in the options from your webpack.config.js, so it works the same
  // as your app's code
  webpackOptions,
  watchOptions: {},
}

module.exports = (on) => {
  on('file:preprocessor', webpack(options))
}

Install dev dependencies

npm i -D @cypress/webpack-preprocessor \
  vue-loader vue-template-compiler css-loader

And write a test

import Hello from '../../components/Hello.vue'
import { mountCallback } from 'cypress-vue-unit-test'

describe('Hello.vue', () => {
  beforeEach(mountCallback(Hello))

  it('shows hello', () => {
    cy.contains('Hello World!')
  })
})

Code coverage

This plugin uses babel-plugin-istanbul to automatically instrument .js and .vue files and generates the code coverage report using dependency cypress-io/code-coverage (included). The output reports are saved in the folder “coverage” at the end of the test run.

If you want to disable code coverage instrumentation and reporting, use --env coverage=false or CYPRESS_coverage=false or set in your cypress.json file

{
  "env": {
    "coverage": false
  }
}

Development

To see all local tests, install dependencies, build the code and open Cypress in GUI mode

npm install
npm run build
npm run cy:open

The build is done using tsc that transpiles all files from src to dist folder.

Debugging

Run Cypress with environment variable

DEBUG=cypress-vue-unit-test

If some deeply nested objects are abbreviated and do not print fully, set the maximum logging depth

DEBUG=cypress-vue-unit-test DEBUG_DEPTH=10

FAQ

  • If your component’s static assets are not loading, you probably need to start and proxy Webpack dev server. See issue #4

Related info

Migration guide

From v2 to v3

  • update cypress/plugins/index.js file to pass the on, config arguments when creating the default preprocessor. See change, in general the new way is:
const {
  onFileDefaultPreprocessor,
} = require('cypress-vue-unit-test/preprocessor/webpack')

module.exports = (on, config) => {
  require('@cypress/code-coverage/task')(on, config)
  on('file:preprocessor', onFileDefaultPreprocessor(config))

  // IMPORTANT to return the config object
  // with the any changed environment variables
  return config
}

Test adapters for other frameworks

Contributors

Download Details:

Author: bahmutov

GitHub: https://github.com/bahmutov/cypress-vue-unit-test

#vuejs #vue #javascript #vue-js

A little helper to unit test Vue components in the Cypress.io E2E test runner

cypress-vue-unit-test

A little helper to unit test Vue components in the open source Cypress.io E2E test runner v4.5.0+

TLDR

  • What is this? This package allows you to use Cypress test runner to unit test your Vue components with zero effort.

Example component test

  • How is this different from vue-test-utils? Vue Test Utils uses Node, requires stubbing browser APIs, and requires users to await Vue’s internal event loop. Cypress Vue Unit Test runs each component in the real browser with full power of Cypress E2E test runner: live GUI, full API, screen recording, CI support, cross-platform.

  • If you like using @testing-library/vue, you can use @testing-library/cypress for the same findBy, queryBy commands, see one of the examples in the list below

Blog posts

Install

Terminal typing vue add cypress-experimental

Vue CLI Installation

Vue CLI v3+

Recommended: One step install to existing projects with Vue CLI via experimental plugin, read Write Your First Vue Component Test

vue add cypress-experimental

If you want to install this package manually, follow manual install

Usage and Examples

// components/HelloWorld.spec.js
import { mount } from 'cypress-vue-unit-test'
import { HelloWorld } from './HelloWorld.vue'
describe('HelloWorld component', () => {
  it('works', () => {
    mount(HelloWorld)
    // now use standard Cypress commands
    cy.contains('Hello World!').should('be.visible')
  })
})

Options

You can pass additional styles, css files and external stylesheets to load, see docs/styles.md for full list.

import Todo from './Todo.vue'
const todo = {
  id: '123',
  title: 'Write more tests',
}

mount(Todo, {
  propsData: { todo },
  stylesheets: [
    'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.css',
  ],
})

See examples below for details.

Global Vue Options

You can pass extensions (global components, mixins, modules to use) when mounting Vue component. Use { extensions: { ... }} object inside the options.

  • components - object of ‘id’ and components to register globally, see Components example
  • use (alias plugins) - list of plugins, see Plugins
  • mixin (alias mixins) - list of global mixins, see Mixins example
  • filters - hash of global filters, see Filters example

The intro example

Take a look at the first Vue v2 example: Declarative Rendering. The code is pretty simple

<div id="app">
  {{ message }}
</div>
var app = new Vue({
  el: '#app',
  data() {
    return { message: 'Hello Vue!' }
  },
})

It shows the message when running in the browser

Hello Vue!

Let’s test it in Cypress.io (for the current version see cypress/integration/spec.js).

import { mountCallback } from 'cypress-vue-unit-test'

describe('Declarative rendering', () => {
  // Vue code from https://vuejs.org/v2/guide/#Declarative-Rendering
  const template = `
    <div id="app">
      {{ message }}
    </div>
  `

  const data = {
    message: 'Hello Vue!',
  }

  // that's all you need to do
  beforeEach(mountCallback({ template, data }))

  it('shows hello', () => {
    cy.contains('Hello Vue!')
  })

  it('changes message if data changes', () => {
    // mounted Vue instance is available under Cypress.vue
    Cypress.vue.message = 'Vue rocks!'
    cy.contains('Vue rocks!')
  })
})

Fire up Cypress test runner and have real browser (Electron, Chrome) load Vue and mount your test code and be able to interact with the instance through the reference Cypress.vue.$data and via GUI. The full power of the Cypress API is available.

Hello world tested

The list example

There is a list example next in the Vue docs.

<div id="app-4">
  <ol>
    <li v-for="todo in todos">
      {{ todo.text }}
    </li>
  </ol>
</div>
var app4 = new Vue({
  el: '#app-4',
  data: {
    todos: [
      { text: 'Learn JavaScript' },
      { text: 'Learn Vue' },
      { text: 'Build something awesome' },
    ],
  },
})

Let’s test it. Simple.

import { mountCallback } from 'cypress-vue-unit-test'

describe('Declarative rendering', () => {
  // List example from https://vuejs.org/v2/guide/#Declarative-Rendering
  const template = `
    <ol>
      <li v-for="todo in todos">
        {{ todo.text }}
      </li>
    </ol>
  `

  function data() {
    return {
      todos: [
        { text: 'Learn JavaScript' },
        { text: 'Learn Vue' },
        { text: 'Build something awesome' },
      ],
    }
  }

  beforeEach(mountCallback({ template, data }))

  it('shows 3 items', () => {
    cy.get('li').should('have.length', 3)
  })

  it('can add an item', () => {
    Cypress.vue.todos.push({ text: 'Test using Cypress' })
    cy.get('li').should('have.length', 4)
  })
})

List tested

Handling User Input

The next section in the Vue docs starts with reverse message example.

<div id="app-5">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">Reverse Message</button>
</div>
var app5 = new Vue({
  el: '#app-5',
  data: {
    message: 'Hello Vue.js!',
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    },
  },
})

We can write the test the same way

import { mountCallback } from 'cypress-vue-unit-test'

describe('Handling User Input', () => {
  // Example from https://vuejs.org/v2/guide/#Handling-User-Input
  const template = `
    <div>
      <p>{{ message }}</p>
      <button v-on:click="reverseMessage">Reverse Message</button>
    </div>
  `

  function data() {
    return { message: 'Hello Vue.js!' }
  }

  const methods = {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')
    },
  }

  beforeEach(mountCallback({ template, data, methods }))

  it('reverses text', () => {
    cy.contains('Hello Vue')
    cy.get('button').click()
    cy.contains('!sj.euV olleH')
  })
})

Take a look at the video of the test. When you hover over the CLICK step the test runner is showing before and after DOM snapshots. Not only that, the application is fully functioning, you can interact with the application because it is really running!

Reverse input

Component example

Let us test a complex example. Let us test a single file Vue component. Here is the Hello.vue file

<template>
  <p>{{ greeting }} World!</p>
</template>

<script>
export default {
  data() {
    return {
      greeting: 'Hello',
    }
  },
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

note to learn how to load Vue component files in Cypress, see Bundling section.

Do you want to interact with the component? Go ahead! Do you want to have multiple components? No problem!

import Hello from '../../components/Hello.vue'
import { mountCallback } from 'cypress-vue-unit-test'
describe('Several components', () => {
  const template = `
    <div>
      <hello></hello>
      <hello></hello>
      <hello></hello>
    </div>
  `
  const components = {
    hello: Hello,
  }
  beforeEach(mountCallback({ template, components }))

  it('greets the world 3 times', () => {
    cy.get('p').should('have.length', 3)
  })
})

Spying example

Button counter component is used in several Vue doc examples

<template>
  <button v-on:click="incrementCounter">{{ counter }}</button>
</template>

<script>
export default {
  data() {
    return {
      counter: 0,
    }
  },

  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    },
  },
}
</script>

<style scoped>
button {
  margin: 5px 10px;
  padding: 5px 10px;
  border-radius: 3px;
}
</style>

Let us test it - how do we ensure the event is emitted when the button is clicked? Simple - let us spy on the event, spying and stubbing is built into Cypress

import ButtonCounter from '../../components/ButtonCounter.vue'
import { mountCallback } from 'cypress-vue-unit-test'

describe('ButtonCounter', () => {
  beforeEach(mountCallback(ButtonCounter))

  it('starts with zero', () => {
    cy.contains('button', '0')
  })

  it('increments the counter on click', () => {
    cy.get('button').click().click().click().contains('3')
  })

  it('emits "increment" event on click', () => {
    const spy = cy.spy()
    Cypress.vue.$on('increment', spy)
    cy.get('button')
      .click()
      .click()
      .then(() => {
        expect(spy).to.be.calledTwice
      })
  })
})

The component is really updating the counter in response to the click and is emitting an event.

Spying test

XHR spying and stubbing

The mount function automatically wraps XMLHttpRequest giving you an ability to intercept XHR requests your component might do. For full documentation see Network Requests. In this repo see components/AjaxList.vue and the corresponding tests cypress/integration/ajax-list-spec.js.

// component use axios to get list of users
created() {
  axios.get(`https://jsonplaceholder.cypress.io/users?_limit=3`)
  .then(response => {
    // JSON responses are automatically parsed.
    this.users = response.data
  })
}
// test can observe, return mock data, delay and a lot more
beforeEach(mountCallback(AjaxList))
it('can inspect real data in XHR', () => {
  cy.server()
  cy.route('/users?_limit=3').as('users')
  cy.wait('@users').its('response.body').should('have.length', 3)
})
it('can display mock XHR response', () => {
  cy.server()
  const users = [{id: 1, name: 'foo'}]
  cy.route('GET', '/users?_limit=3', users).as('users')
  cy.get('li').should('have.length', 1)
    .first().contains('foo')
})

Spying on window.alert

Calls to window.alert are automatically recorded, but do not show up. Instead you can spy on them, see AlertMessage.vue and its test cypress/integration/alert-spec.js

Comparison

Feature Vue Test Utils or @testing-library/vue Cypress + cypress-vue-unit-test
Test runs in real browser
Uses full mount
Test speed 🏎 as fast as the app works in the browser
Test can use additional plugins maybe use any Cypress plugin
Test can interact with component synthetic limited API use any Cypress command
Test can be debugged via terminal and Node debugger use browser DevTools
Built-in time traveling debugger Cypress time traveling debugger
Re-run tests on file or test change
Test output on CI terminal terminal, screenshots, videos
Tests can be run in parallel ✅ via parallelization
Test against interface if using @testing-library/vue ✅ and can use @testing-library/cypress
Spying and mocking Jest mocks Sinon library
Code coverage

Examples

// components/HelloWorld.spec.js
import { mount } from 'cypress-vue-unit-test'
import { HelloWorld } from './HelloWorld.vue'
describe('HelloWorld component', () => {
  it('works', () => {
    mount(HelloWorld)
    // now use standard Cypress commands
    cy.contains('Hello World!').should('be.visible')
  })
})

Basic examples

Spec Description
Components Registers global components to use
Filters Registering global filters
Hello Testing examples from Vue2 cookbook
Mixins Registering Vue mixins
Plugins Loading additional plugins
Props Pass props to the component during mount
Slots Passing slots and scopedSlots to the component
Small examples A few small examples testing forms, buttons

Advanced examples

Spec Description
access-component Access the mounted component directly from test
i18n Testing component that uses Vue I18n plugin
mocking-axios Mocking 3rd party CommonJS modules like axios
mocking-fetch Mocking window.fetch to stub responses and test the UI
fetch-polyfill Using experimental fetch polyfill to spy on / stub those Ajax requests using regular Cypress network methods
mocking-components Mocking locally registered child components during tests
mocking-imports Stub ES6 imports from the tests
render-functions Mounting components with a render function

Full examples

We have several subfolders in examples folder.

Folder Name Description
cli An example app scaffolded using Vue CLI and the component testing added using vue add cypress-experimental command.

External examples

Repo Description
vue-component-test-example Scaffolded Vue CLI v3 project with added component tests, read Write Your First Vue Component Test.

Known problems

Bundling

How do we load this Vue file into the testing code? Using webpack preprocessor. Note that this module ships with @cypress/webpack-preprocessor 2.x that requires Webpack 4.x. If you have Webpack 3.x please add @cypress/webpack-preprocessor v1.x.

Short way

For Webpack Users

Your project probably already has webpack.config.js setup to transpile .vue files. To load these files in the Cypress tests, grab the webpack processor included in this module, and load it from the cypress/plugins/index.js file.

const {
  onFilePreprocessor,
} = require('cypress-vue-unit-test/preprocessor/webpack')
module.exports = (on) => {
  on('file:preprocessor', onFilePreprocessor('../path/to/webpack.config'))
}

Cypress should be able to import .vue files in the tests

Manual

Using @cypress/webpack-preprocessor and vue-loader. You can use cypress/plugins/index.js to load .vue files using vue-loader.

// cypress/plugins/index.js
const webpack = require('@cypress/webpack-preprocessor')
const webpackOptions = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
    ],
  },
}

const options = {
  // send in the options from your webpack.config.js, so it works the same
  // as your app's code
  webpackOptions,
  watchOptions: {},
}

module.exports = (on) => {
  on('file:preprocessor', webpack(options))
}

Install dev dependencies

npm i -D @cypress/webpack-preprocessor \
  vue-loader vue-template-compiler css-loader

And write a test

import Hello from '../../components/Hello.vue'
import { mountCallback } from 'cypress-vue-unit-test'

describe('Hello.vue', () => {
  beforeEach(mountCallback(Hello))

  it('shows hello', () => {
    cy.contains('Hello World!')
  })
})

Code coverage

This plugin uses babel-plugin-istanbul to automatically instrument .js and .vue files and generates the code coverage report using dependency cypress-io/code-coverage (included). The output reports are saved in the folder “coverage” at the end of the test run.

If you want to disable code coverage instrumentation and reporting, use --env coverage=false or CYPRESS_coverage=false or set in your cypress.json file

{
  "env": {
    "coverage": false
  }
}

Note ⚠️: if the component .vue file does not have a <script> section, it will not have any code coverage information.

Development

To see all local tests, install dependencies, build the code and open Cypress in GUI mode

npm install
npm run build

The build is done using tsc that transpiles all files from src to the dist folder. You can then run component tests by opening Cypress

npm run cy:open

and clicking on any component spec

Component specs

Larger tests that use full application and run on CI (see circle.yml) are located in the folder examples.

Debugging

Run Cypress with environment variable

DEBUG=cypress-vue-unit-test

If some deeply nested objects are abbreviated and do not print fully, set the maximum logging depth

DEBUG=cypress-vue-unit-test DEBUG_DEPTH=10

FAQ

  • If your component’s static assets are not loading, you probably need to start and proxy Webpack dev server. See issue #4

Related info

Migration guide

From v2 to v3

  • update cypress/plugins/index.js file to pass the on, config arguments when creating the default preprocessor. See change, in general the new way is:
const {
  onFileDefaultPreprocessor,
} = require('cypress-vue-unit-test/preprocessor/webpack')

module.exports = (on, config) => {
  require('@cypress/code-coverage/task')(on, config)
  on('file:preprocessor', onFileDefaultPreprocessor(config))

  // IMPORTANT to return the config object
  // with the any changed environment variables
  return config
}

Test adapters for other frameworks

Contributors

Download Details:

Author: bahmutov

Source Code: https://github.com/bahmutov/cypress-vue-unit-test

#vuejs #vue #javascript

Software Testing 101: Regression Tests, Unit Tests, Integration Tests

Automation and segregation can help you build better software
If you write automated tests and deliver them to the customer, he can make sure the software is working properly. And, at the end of the day, he paid for it.

Ok. We can segregate or separate the tests according to some criteria. For example, “white box” tests are used to measure the internal quality of the software, in addition to the expected results. They are very useful to know the percentage of lines of code executed, the cyclomatic complexity and several other software metrics. Unit tests are white box tests.

#testing #software testing #regression tests #unit tests #integration tests

Tamia  Walter

Tamia Walter

1596754901

Testing Microservices Applications

The shift towards microservices and modular applications makes testing more important and more challenging at the same time. You have to make sure that the microservices running in containers perform well and as intended, but you can no longer rely on conventional testing strategies to get the job done.

This is where new testing approaches are needed. Testing your microservices applications require the right approach, a suitable set of tools, and immense attention to details. This article will guide you through the process of testing your microservices and talk about the challenges you will have to overcome along the way. Let’s get started, shall we?

A Brave New World

Traditionally, testing a monolith application meant configuring a test environment and setting up all of the application components in a way that matched the production environment. It took time to set up the testing environment, and there were a lot of complexities around the process.

Testing also requires the application to run in full. It is not possible to test monolith apps on a per-component basis, mainly because there is usually a base code that ties everything together, and the app is designed to run as a complete app to work properly.

Microservices running in containers offer one particular advantage: universal compatibility. You don’t have to match the testing environment with the deployment architecture exactly, and you can get away with testing individual components rather than the full app in some situations.

Of course, you will have to embrace the new cloud-native approach across the pipeline. Rather than creating critical dependencies between microservices, you need to treat each one as a semi-independent module.

The only monolith or centralized portion of the application is the database, but this too is an easy challenge to overcome. As long as you have a persistent database running on your test environment, you can perform tests at any time.

Keep in mind that there are additional things to focus on when testing microservices.

  • Microservices rely on network communications to talk to each other, so network reliability and requirements must be part of the testing.
  • Automation and infrastructure elements are now added as codes, and you have to make sure that they also run properly when microservices are pushed through the pipeline
  • While containerization is universal, you still have to pay attention to specific dependencies and create a testing strategy that allows for those dependencies to be included

Test containers are the method of choice for many developers. Unlike monolith apps, which lets you use stubs and mocks for testing, microservices need to be tested in test containers. Many CI/CD pipelines actually integrate production microservices as part of the testing process.

Contract Testing as an Approach

As mentioned before, there are many ways to test microservices effectively, but the one approach that developers now use reliably is contract testing. Loosely coupled microservices can be tested in an effective and efficient way using contract testing, mainly because this testing approach focuses on contracts; in other words, it focuses on how components or microservices communicate with each other.

Syntax and semantics construct how components communicate with each other. By defining syntax and semantics in a standardized way and testing microservices based on their ability to generate the right message formats and meet behavioral expectations, you can rest assured knowing that the microservices will behave as intended when deployed.

Ways to Test Microservices

It is easy to fall into the trap of making testing microservices complicated, but there are ways to avoid this problem. Testing microservices doesn’t have to be complicated at all when you have the right strategy in place.

There are several ways to test microservices too, including:

  • Unit testing: Which allows developers to test microservices in a granular way. It doesn’t limit testing to individual microservices, but rather allows developers to take a more granular approach such as testing individual features or runtimes.
  • Integration testing: Which handles the testing of microservices in an interactive way. Microservices still need to work with each other when they are deployed, and integration testing is a key process in making sure that they do.
  • End-to-end testing: Which⁠—as the name suggests⁠—tests microservices as a complete app. This type of testing enables the testing of features, UI, communications, and other components that construct the app.

What’s important to note is the fact that these testing approaches allow for asynchronous testing. After all, asynchronous development is what makes developing microservices very appealing in the first place. By allowing for asynchronous testing, you can also make sure that components or microservices can be updated independently to one another.

#blog #microservices #testing #caylent #contract testing #end-to-end testing #hoverfly #integration testing #microservices #microservices architecture #pact #testing #unit testing #vagrant #vcr

A Secret Weapon for Vue Unit Tests

If your Vue single-file components have dependencies, you’ll need to handle the dependencies somehow when you unit test the component.

One approach is to install the dependencies in the test environment, but this may overcomplicate your tests.

In this article, I’ll show you how to mock a module file in Jest by replacing it in your component’s graph of dependencies.

#vue.js #jest #testing #unit testing #vue