0
0
Cypresstesting~15 mins

Dual commands in Cypress - Deep Dive

Choose your learning style9 modes available
Overview - Dual commands
What is it?
Dual commands in Cypress are a way to combine two actions into one command chain. They allow you to perform an action and then immediately assert or continue with another action seamlessly. This helps write cleaner and more readable test code by chaining commands that work together.
Why it matters
Without dual commands, test code can become repetitive and harder to read because you would write separate commands for actions and assertions. Dual commands solve this by making tests concise and easier to maintain, reducing errors and improving test clarity. This leads to faster debugging and more reliable tests.
Where it fits
Before learning dual commands, you should understand basic Cypress commands and how command chaining works. After mastering dual commands, you can explore custom commands and advanced Cypress features like retries and network stubbing to build robust test suites.
Mental Model
Core Idea
Dual commands let you perform an action and immediately follow it with another related action or assertion in one smooth chain.
Think of it like...
It's like using a remote control that not only turns on the TV but also sets the volume right after, all with one button press.
┌───────────────┐     ┌───────────────┐
│  Action Cmd   │────▶│  Follow-up    │
│ (e.g., click) │     │ (e.g., assert)│
└───────────────┘     └───────────────┘
         │                    │
         └─────────Chain──────┘
Build-Up - 6 Steps
1
FoundationUnderstanding Cypress Command Chains
🤔
Concept: Learn how Cypress commands are chained to perform sequential actions.
In Cypress, commands like cy.get(), cy.click(), and cy.contains() are chained together. Each command waits for the previous one to finish before running. For example, cy.get('button').click() finds a button and clicks it.
Result
Commands run one after another, ensuring the test flows logically and waits for elements to be ready.
Understanding command chaining is essential because dual commands build on this idea to combine actions and assertions smoothly.
2
FoundationBasic Cypress Assertions
🤔
Concept: Learn how to check if elements meet conditions using assertions.
Cypress uses assertions like .should('be.visible') or .should('contain', 'Submit') to verify element states or content. These assertions can be chained after commands to check results.
Result
You can confirm that elements are visible, contain text, or have attributes as expected.
Knowing assertions lets you verify outcomes immediately after actions, which dual commands leverage to combine steps.
3
IntermediateCombining Actions and Assertions
🤔Before reading on: do you think you can chain an action and an assertion in one command line? Commit to yes or no.
Concept: Dual commands let you chain an action and an assertion directly, making tests concise.
For example, cy.get('button').click().should('be.disabled') clicks a button and then checks if it is disabled. This combines two steps into one chain.
Result
Tests become shorter and easier to read, with related steps grouped together.
Combining actions and assertions reduces repetition and improves test clarity by showing cause and effect in one place.
4
IntermediateUsing Dual Commands with Custom Commands
🤔Before reading on: can custom commands also use dual command chaining? Commit to yes or no.
Concept: You can create custom Cypress commands that perform dual commands internally for reuse.
For example, Cypress.Commands.add('clickAndCheck', (selector) => { return cy.get(selector).click().should('have.class', 'active') }); Then use cy.clickAndCheck('button') in tests.
Result
Custom commands encapsulate dual commands, making tests DRY (Don't Repeat Yourself) and easier to maintain.
Knowing that dual commands can be wrapped in custom commands helps build scalable test suites.
5
AdvancedHandling Timing with Dual Commands
🤔Before reading on: do you think dual commands automatically handle waiting for elements? Commit to yes or no.
Concept: Dual commands benefit from Cypress's automatic waiting, but understanding timing is key to avoid flaky tests.
Cypress waits for elements before actions and assertions. For example, cy.get('button').click().should('be.visible') waits for the button to appear before clicking and asserting. However, if the assertion depends on a delayed state change, you may need explicit waits or retries.
Result
Tests run reliably without manual waits most of the time, but timing issues can still cause failures if misunderstood.
Understanding Cypress's waiting behavior with dual commands prevents flaky tests and improves stability.
6
ExpertDual Commands Internals and Command Queue
🤔Before reading on: do you think dual commands run synchronously or are queued internally? Commit to your answer.
Concept: Dual commands are queued in Cypress's command queue and run asynchronously with automatic retries and error handling.
Cypress maintains a command queue where each command, including dual commands, is added. Commands run asynchronously but appear synchronous in code. Cypress retries assertions automatically until they pass or timeout. Dual commands benefit from this queue and retry logic, making tests robust.
Result
Tests using dual commands are more reliable due to automatic retries and asynchronous execution managed by Cypress.
Knowing the command queue and retry mechanism explains why dual commands work smoothly and how to debug timing issues.
Under the Hood
Cypress commands, including dual commands, are added to an internal command queue. Each command runs asynchronously but appears synchronous to the user. When a dual command is used, Cypress executes the first action, then immediately queues the assertion or follow-up command. Cypress automatically waits for elements to be ready and retries assertions until they pass or timeout. This mechanism ensures smooth chaining and reliable test execution.
Why designed this way?
Cypress was designed to simplify asynchronous testing by hiding callback hell and promises. The command queue and automatic retries make tests easier to write and maintain. Dual commands fit this design by allowing combined actions and assertions without breaking the chain or requiring manual waits. Alternatives like manual waits or callbacks were error-prone and less readable.
┌───────────────┐     ┌───────────────┐     ┌───────────────┐
│ Command Queue │────▶│ Execute Action│────▶│ Execute Assert│
│  (cy.get)    │     │  (click)      │     │ (should)      │
└───────────────┘     └───────────────┘     └───────────────┘
         │                    │                    │
         └─────────Asynchronous Execution─────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do you think dual commands run instantly and synchronously? Commit to yes or no.
Common Belief:Dual commands run instantly and synchronously like normal code lines.
Tap to reveal reality
Reality:Dual commands are queued and run asynchronously with automatic retries and waits.
Why it matters:Assuming synchronous execution leads to misunderstanding test failures and flaky tests due to timing issues.
Quick: Can you chain any two commands arbitrarily as dual commands? Commit to yes or no.
Common Belief:Any two Cypress commands can be chained as dual commands without restrictions.
Tap to reveal reality
Reality:Only commands that return subjects and support chaining can be combined; some commands do not support chaining or assertions.
Why it matters:Trying to chain incompatible commands causes test errors and confusion.
Quick: Do you think dual commands eliminate the need for explicit waits? Commit to yes or no.
Common Belief:Dual commands always handle timing perfectly, so explicit waits are never needed.
Tap to reveal reality
Reality:While Cypress waits automatically, some complex timing scenarios still require explicit waits or retries.
Why it matters:Ignoring timing nuances can cause flaky tests that pass or fail unpredictably.
Quick: Do you think custom commands cannot use dual commands internally? Commit to yes or no.
Common Belief:Custom commands are separate and cannot combine actions and assertions like dual commands.
Tap to reveal reality
Reality:Custom commands can and often do use dual commands internally to simplify test code.
Why it matters:Missing this limits test code reuse and clarity.
Expert Zone
1
Dual commands rely on Cypress's automatic retry mechanism, but understanding when retries happen helps optimize test speed and reliability.
2
Some Cypress commands return 'void' and cannot be chained with assertions, so knowing which commands support dual chaining is crucial.
3
Custom dual commands can accept parameters and return Cypress chains, enabling flexible and reusable test building blocks.
When NOT to use
Dual commands are not suitable when you need to perform unrelated actions or when commands do not return chainable subjects. In such cases, separate commands or explicit waits should be used. Also, for complex asynchronous flows involving network stubbing or multiple page transitions, breaking commands into clear steps improves readability.
Production Patterns
In real-world tests, dual commands are used to combine user interactions with immediate assertions, like clicking a button and verifying its disabled state. Teams often wrap dual commands into custom commands for common UI patterns, improving test maintainability. Advanced usage includes chaining dual commands with network request assertions to verify backend responses after UI actions.
Connections
Promise chaining in JavaScript
Dual commands in Cypress build on the idea of chaining asynchronous operations like promises.
Understanding promise chaining helps grasp how Cypress queues commands and manages asynchronous test steps smoothly.
Assembly line workflow
Dual commands resemble an assembly line where one step immediately leads to the next related step.
Seeing dual commands as a workflow helps understand how combining actions and checks improves efficiency and clarity.
Human multitasking
Dual commands mimic how humans perform an action and immediately check the result without pausing.
This connection shows how dual commands make automated tests feel more natural and intuitive.
Common Pitfalls
#1Chaining commands that do not return chainable subjects causes errors.
Wrong approach:cy.visit('/page').click().should('be.visible')
Correct approach:cy.visit('/page'); cy.get('button').click().should('be.visible')
Root cause:cy.visit() does not return a chainable subject for click(), so chaining click() directly fails.
#2Assuming dual commands remove the need for waits in all cases.
Wrong approach:cy.get('button').click().should('have.class', 'active') // fails if class changes late
Correct approach:cy.get('button').click(); cy.wait(500); cy.get('button').should('have.class', 'active')
Root cause:Automatic waits do not cover delayed UI updates; explicit waits or retries are needed.
#3Writing custom commands without returning the Cypress chain breaks chaining.
Wrong approach:Cypress.Commands.add('clickAndCheck', (selector) => { cy.get(selector).click().should('be.visible') });
Correct approach:Cypress.Commands.add('clickAndCheck', (selector) => { return cy.get(selector).click().should('be.visible'); });
Root cause:Not returning the chain breaks Cypress's command queue and chaining.
Key Takeaways
Dual commands in Cypress combine actions and assertions into one chain for cleaner, more readable tests.
They rely on Cypress's command queue and automatic retry mechanism to run asynchronously and reliably.
Not all commands support chaining; understanding which do prevents errors and flaky tests.
Custom commands can use dual commands internally to create reusable test building blocks.
Knowing when dual commands fit and when to separate steps improves test clarity and stability.