In Cypress when to use Custom Command vs Task?

A command (most methods on the global cy object) is a function that enqueues (pushes) an action to a queue of currently-executing commands. The queue executes serially and asynchronously (that's why return value of a command is an object having a .then method --- but despite that and the fact it behaves like promise, it's not a promise). Until a previous command is finished, the next command doesn't execute.

Commands are defined and executed directly in the browser.

A custom command is a regular command, but defined by you as opposed to the default commands that Cypress supplies out of the box. Custom commands are useful for automating a workflow you repeat in your tests over and over (e.g. by grouping several default cy commands together).

Commands are used to interact with your web app under test (AUT) --- most notably with the DOM (e.g. via cy.get(selector) to query the DOM), and to make assertions.

It's also important to realize that while commands are being executed serially, they are enqueued immediately (in the same event loop tick), and any expressions you pass to them are evaluated then and there. This isn't a Cypress-specific behavior, just plain JavaScript. That's why you can't do things like these:

let value;
cy.get('.myInput').invoke('val').then(val => value = val);
cy.get('.mySecondInput').type(value); // ✗ value is undefined here

Nor can you use async/await:

let value;
// ✗ doesn't work on Cypress commands
const value = await cy.get('.myInput').invoke('val');

A task is a function defined and executed on the Cypress backend process (Node.js), not in the browser.

To execute a task (which you previously defined in your cypress/plugins/index.js file), you need to first enqueue it as a regular command in your test via cy.task(taskName, data). Cypress then (when the command takes its turn to execute) sends a message to the backend process where the task is executed.

Data your task returns is serialized (via JSON.stringify or something similar) and sent back to the browser where it's passed to a callback you potentially chained to your cy.task() command using .then(callback).

Tasks are mainly used to communicate with your own server backend, to e.g. seed the database; or for I/O such as reading/writing to a file (although cypress supplies commands for these such as cy.exec() or cy.writeFile()).

There are no default tasks --- every task you execute you first need to define yourself.

Another important point is that the messages that are sent between processes (the Cypress browser process, and the Cypress node process) are sent via an IPC channel, and must be serializable. That means that the data you pass to cy.task(taskName, data) are stringified, as well as is the response returned from the task itself. Thus, sending e.g. an object containing a method will not work (that is, the method won't be transferred at all).

Great answers but in order to sum up, here are two main differences that help you choose whether you need a Cypress command or a task :

  • If you need to run a promise or interact with your backend, go with a task.
  • If you are interacting with the DOM and making assertions, go with a command.

Taken from this article