Max Schmitt

October 27 2020

Cypress: How to interact with a database during your tests

Cypress is different from a lot of other integration testing frameworks like WebdriverIO in that your actual testing code (where you're interacting with the page and making assertions) runs in a browser context instead of Node.js.

So how would you write server-side setup code then, that interacts with the database for example?

Cypress tasks

If you need to run server-side code during your tests, you can use Cypress' task command.

Here's the quick rundown.

There's two parts to every Cypress task:

1. Writing the server-side implementation

Cypress' plugin-file (usually cypress/plugins/index.js) runs in a Node.js context. Inside here, you can listen for tasks and then execute arbitrary (optionally asynchronous) code.

cypress/plugins/index.js

module.exports = async (on, config) => {
const db = await Db.connect()
on('task', {
async createUser(userData) {
const result = await db.collection('users').insertOne(userData)
return result
},
})
}

2. Calling the Cypress task during your test

To call a Cypress task, you can use the cy.task() command in your spec-file:

cypress/integration/login.spec.js

describe('Login', function () {
it('redirects to the dashboard after login', function () {
const userData = {
name: "Max"
email: 'max@maxschmitt.me'
}
cy.task('createUser', userData).then((result) => {
cy.visit('/login')
cy.fillLoginForm(userData)
cy.get('button[type="submit"]').click()
cy.url().should('include', '/dashboard')
})
})
})

Good to know

  • Cypress tasks should return something JSON-serializable
  • Cypress tasks must return something other than undefined
  • Cypress tasks can accept one parameter that is JSON-serializable
  • Cypress tasks, like other Cypress commands, run asynchronously but can't be used with async/await
  • Cypress tasks can run during test hooks like beforeEach

Organizing your Cypress tasks

As your test suite grows, it becomes important that you cleanly organize your Cypress tasks.

It can prove a bit difficult because Cypress tasks operate on a global namespace and they aren't opinionated on how you should organize your tasks on the file level.

To help with this problem, I made cypress-routines. It lets you write so-called "routines" (instead of tasks) per spec-file in an accompanying routines-files.

cypress/integration/
login.spec.js
login.routines.js
signup.spec.js
signup.routines.js
dashboard.spec.js
dashboard.routines.js

Read my blog post about cypress-routines to learn more.

You can also check out the docs or watch this video that shows cypress-routines in action.

Links