And the Oscar goes to…
ACT 1: EXPOSITION
On numerous occasions, colleagues have come to me with a question that seems to resonate among many Cypress users: Which approach is best for reusing actions or assertions when writing tests? Should they opt for a JavaScript Utility Function, a Custom Command, perhaps a Custom Query, or even one of the so-called Tasks? What about an External Plugin?
This query isn’t unique to my circle; it’s a topic that even once in a while surfaces in the Cypress.io Discord community. The myriad of methods available in Cypress can be overwhelming, but this versatility is also what makes this tool so powerful.
If you’re reading this blog post in the hope of finding a definitive answer as to which Cypress helper deserves the “Oscar,” then this might be the point where you choose to stop reading and check back in my next entry. After all, when it comes to movie tastes, nothing is written in stone.
However, if you’re open to exploring some recommendations and understanding when one method might be more advantageous than another, then I believe you are be in the right place.
I had the idea that presenting a sneak peek of each Cypress Helper on a ‘single screen’ might just help you decide which one to ‘watch’ the full feature film based on your mood or needs on any given day.
My hope is that by the end of this post, you will have a stronger set of strategies to enhance your Cypress toolkit for everyday use. So get the popcorn ready and enjoy the previews. 🍿
ACT 2: CONFRONTATION
In Cypress.io, the decision to use a JavaScript Utility Function, Custom Command, Custom Query, Task or even an External Plugin should depend on the specific needs of your test suite and the scope of functionality you are aiming to achieve.
So… getting to the point, in what situations can each of them lend you a better hand and save you a lot of work?
Let’s look at them one by one.
JavaScript Utility Function
Use JavaScript Utility Functions for simple, synchronous operations that don’t need to interact with the Cypress command chain.
Ideal for data transformations, calculations, or any generic JavaScript functions.
You could define them directly in your test spec for cases where the operation is very specific to the scope of those tests, or wrap several of them in a helper file in the
cypress/support
folder if you plan to use them across multiple test specs.
There is nothing preventing you from returning a Cypress chainable object from the call to JavaScript Utility Functions, and in certain cases, it might even be quite convenient.
However, given that the majority of JavaScript code is synchronous, and considering that the nature of Cypress commands is asynchronous and they get queued for execution at a later time, you might want to consider using a Custom Command instead of a JavaScript Utility Function when returning a Cypress chainable object.
Custom Command
This is when things start to get interesting…
Use Custom Commands to create reusable sets of Cypress commands that can be called as a single command.
They are asynchronous, and they can return a Cypress chainable object, over which you can run assertions.
A Custom Command can: start a new Cypress chain (called a parent command), receive a previous subject and continue an existing chain (called a child command), or either start a chain or use an existing chain (called a dual command).
Added via
Cypress.Commands.add()
and becomes part of the Cypress chainable interface. You can also overwrite existing commands usingCypress.Commands.overwrite()
.They are defined in the
cypress/support/commnads.js
and are extremely helpful for actions performed frequently in your tests, like custom login procedures or form submissions.They can be reused across multiple test specs in your Cypress project.
BE AWARE! Custom Commands are executed once and do not have built-in retry-ability. If you want your method to have retry-ability, it is better to use a Custom Query.
If you would like to dig deeper in the intricacies of Custom Commands you can visit the Cypress documentation Custom Commands, Building Cypress Commands, and Custom Cypress Command Examples.
Custom Query
Custom Queries were a special ‘feature film’ introduced in the Cypress series at the end of 2022, debuting in version 12.
Use Custom Queries to query the state of your application, for instance, to find elements based on custom logic or conditions not covered by Cypress build-in queries. Examples of built-in queries include
get()
,find()
,filter()
,url()
, andwindow()
.They can be particularly useful when integrating with UI libraries or frameworks that require specific selectors or patterns to interact with their components.
Queries are synchronous and can return a Cypress chainable object, over which you can also run assertions.
Custom Queries are retry-able, meaning they will continuously attempt to retrieve whatever you have requested until they succeed or a timeout is reached. It is important that the Custom Query callback function does not change the state of your application.
New queries are added via
Cypress.Commands.addQuery()
, but you can overwrite an existing query usingCypress.Commands.overwriteQuery()
.They are defined in the cypress/support/commands.js file and are useful when you can encapsulate complex or repeated DOM queries that you will reuse across your test framework.
However, for repeatable behavior, it is often more efficient to write a JavaScript Utility function rather than a Custom Query, and after all both behave “synchronously” (by default, JavaScript is a synchronous).
TAKE CAUTION! If your method needs to be asynchronous or only to be called once, then you should write a Custom Command instead.
STAY ALERT ! When piecing together lengthy sequences of queries, ensure that you avoid incorporating standard Cypress commands, as their inclusion will disrupt the test’s ability to retry the full chain.
For more information about Custom Queries you can visit the Cypress documentation Custom Queries and Retry-ability.
Task
Tasks are used for handling operations that need to be executed outside the browser context.
They run in Node.js, executed by the Cypress process, and can be invoked from your tests using the
cy.task()
command. This bridges the gap between the Node.js server and browser-based tests, enabling a more comprehensive testing strategy.Tasks
are ideal for database operations such as seeding, querying, or cleanup. They are also useful for file system interactions, such as downloading files, or any server-side feature not accessible within the browser.Additionally, they are great for storing state in Node that needs to persist between spec files, running parallel tasks like making multiple HTTP requests, and for executing an external process or system command.
The Task event handler can return a value or a promise. Returning undefined, or a promise resolved with undefined, will cause the command to fail.
Tasks are typically defined in the project’s
cypress.config.js
file within thesetupNodeEvents()
function. Alternatively, you can define them in the project'scypress/plugins/index.js
file.
For more information about Tasks you can visit the Cypress documentation Tasks and Real World Example tasks.
External Plugin
Cypress External Plugins are used to extend the functionality of Cypress tests beyond the capabilities provided by the core framework.
These plugins are typically installed via Node Package Manager and configured within the Cypress project to provide additional capabilities tailored to specific testing needs. They help make Cypress a more powerful and versatile tool for end-to-end testing.
You can host your External Plugins on multiple repositories such as GitHub or Bitbucket, and distribute them publicly via the software registry NPM or internally within your organization via Nexus.
They are extraordinarily useful when you have common tools, commands, and assertions that will be reused across multiple Cypress frameworks.
There is a vast array of Cypress External Plugins available; some are very well-maintained and supported by the Cypress community, while others… are not maintained at all.
Therefore, be selective and critical when choosing a Cypress plugin for your application. I recommend opting for plugins that are clean, light-weighted, frequently updated, and supported by credible creators. After all, each time you load a plugin in your test or framework, you are adding time to your test run.
Some common uses for External Plugins in Cypress include:
✔️ Visual Testing (such as Applitools’ cypress-eyes
)
✔️ Accessibility Testing (such as Andy’s cypress-axe
, which utilizes Deque's axe-core
).
✔️ Reporting (such as Yousaf’s cypress-multi-reporters
for generating more informative and styled test reports)
✔️ API Testing (such as Filip’s cypress-plugin-api
)
✔️ A Toolkit of useful extra Query Commands (such as Gleb’s cypress-map
)
✔️ Firing native system events (such as Dmitriy’s cypress-real-events
)
✔️ Filtering Tests (such as @cypress/grep
)
For more information about available External Plugins, you can visit the Cypress documentation on Plugins (however this list seems a little dated).
ACT3: RESOLUTION
Each of these Cypress Tools serves a different purpose and can be used in conjunction to create a robust and maintainable testing suite. So, in my opinion, ALL OF THEM truly deserve to share the “Oscar” for “Best Cypress Helper”. 🏆
JavaScript Utility Functions, Custom Commands and Custom Queries are primarily about organizing code within your tests, while Tasks and External Plugins are for interacting with the system and environment outside the browser or for extending Cypress’s capabilities.
Custom Commands can enhance the readability of your tests by tackling repetitive sets of commands at once. Similarly, Custom Queries can abstract the querying logic, making the tests easier to understand at a glance.
Remember to use Custom Commands and Custom Queries judiciously, as each addition to your testing framework increases the maintenance overhead and can potentially introduce complexity. Keep them well-documented and ensure they provide clear value over the standard set of queries provided by Cypress.
However, I have to say that Cypress External Plugins hold a special place in my 💖, and that’s why I will dedicate a full blog post to them in the future.
Disclaimer: Keanu Reeves has neither won an Oscar, Golden Globe, or Emmy, and nor even been nominated for any of these awards. Maybe in his next John Wick feature film! 🤞 😉
(Image from Marvelous Videos)
Don't forget to leave a comment, give a thumbs up, or follow my Cypress blog if you found this post useful.
Happy reading!
Top comments (0)