Bird
Raised Fist0
Cypresstesting~15 mins

Multiple assertions chaining in Cypress - Deep Dive

Choose your learning style10 modes available
Overview - Multiple assertions chaining
What is it?
Multiple assertions chaining means checking several conditions one after another on the same element or value in a test. In Cypress, you can write these checks in a chain, making tests clear and easy to read. Each assertion confirms a part of the expected behavior, like checking text, visibility, or attributes. This helps ensure the tested feature works exactly as intended.
Why it matters
Without chaining multiple assertions, tests become longer, harder to read, and more error-prone. If you test each condition separately, you might miss how they relate or slow down your tests. Chaining keeps tests concise and efficient, catching problems early and saving time. It makes test reports clearer, showing exactly which part failed.
Where it fits
Before learning multiple assertions chaining, you should know basic Cypress commands and simple assertions. After mastering chaining, you can explore advanced Cypress features like custom commands, retries, and conditional testing. This topic fits in the middle of your Cypress learning path, bridging simple tests and complex test suites.
Mental Model
Core Idea
Chaining multiple assertions in Cypress lets you check many expected conditions on the same subject smoothly and clearly in one flow.
Think of it like...
It's like checking off multiple items on a shopping list while walking through the store aisle by aisle, without going back and forth.
cy.get('selector')
  ├─ .should('be.visible')
  ├─ .and('contain.text', 'Hello')
  └─ .and('have.class', 'active')
Build-Up - 7 Steps
1
FoundationBasic single assertion usage
🤔
Concept: Learn how to write a single assertion in Cypress to check one condition.
In Cypress, you use commands like cy.get() to find elements, then .should() to assert something about them. For example, cy.get('button').should('be.visible') checks if a button is visible on the page.
Result
The test passes if the button is visible; otherwise, it fails and shows an error.
Understanding single assertions is the base for chaining multiple checks later.
2
FoundationUnderstanding Cypress command chaining
🤔
Concept: Cypress commands return subjects that allow chaining more commands or assertions.
When you write cy.get('input'), Cypress finds the input element and passes it along. You can then chain .should() or other commands to keep working with that element without repeating the selector.
Result
Commands flow smoothly, making tests shorter and easier to read.
Knowing how Cypress chains commands helps you write multiple assertions on the same element without extra code.
3
IntermediateChaining multiple assertions with .should() and .and()
🤔Before reading on: do you think .should() can be used multiple times in a chain, or only once?
Concept: You can chain multiple assertions using .should() once and .and() for the rest to check many conditions on one element.
Example: cy.get('h1') .should('be.visible') .and('contain.text', 'Welcome') .and('have.class', 'title') This checks visibility, text content, and CSS class in one chain.
Result
If all assertions pass, the test passes; if any fail, Cypress reports exactly which one failed.
Using .and() after .should() keeps tests concise and groups related checks logically.
4
IntermediateUsing multiple .should() calls in a chain
🤔Before reading on: do you think multiple .should() calls run sequentially or independently?
Concept: You can use multiple .should() calls in a chain, each running its own assertion on the subject.
Example: cy.get('input') .should('be.enabled') .should('have.value', '') Each .should() runs separately but on the same element found by cy.get().
Result
Both assertions must pass for the test to succeed.
Knowing that multiple .should() calls work sequentially helps you organize complex checks clearly.
5
IntermediateChaining assertions on different subjects
🤔Before reading on: can you chain assertions on different elements without repeating cy.get()?
Concept: Chaining assertions works on the same subject; to test different elements, you need separate cy.get() calls.
Example: cy.get('button').should('be.visible') cy.get('input').should('be.enabled') You cannot chain these two assertions directly because they target different elements.
Result
Tests remain clear by separating element queries and their assertions.
Understanding subject chaining limits prevents confusing test failures and keeps tests maintainable.
6
AdvancedCustom assertion chaining with callbacks
🤔Before reading on: do you think you can write your own assertion logic inside .should() callbacks?
Concept: Cypress allows writing custom assertions inside .should() by passing a function that receives the subject and runs multiple checks.
Example: cy.get('ul').should(($list) => { expect($list).to.have.length(1) expect($list.find('li')).to.have.length.greaterThan(2) }) This runs multiple assertions inside one .should() call.
Result
You get flexible, powerful checks beyond built-in assertions.
Custom callbacks let you combine complex logic in one place, improving test expressiveness.
7
ExpertHandling asynchronous retries in chained assertions
🤔Before reading on: do chained assertions retry automatically if they fail initially in Cypress?
Concept: Cypress retries assertions automatically until they pass or timeout, even in chained assertions, making tests stable against slow UI changes.
Example: cy.get('button') .should('be.visible') .and('not.be.disabled') If the button is not visible or disabled at first, Cypress waits and retries until conditions are met or timeout occurs.
Result
Tests become more reliable without manual waits or delays.
Knowing Cypress retries chained assertions helps you write robust tests that handle real-world timing issues gracefully.
Under the Hood
Cypress commands queue up and run asynchronously. When you chain assertions like .should() and .and(), Cypress waits for the subject to appear and then runs each assertion in order. If an assertion fails, Cypress retries the entire chain until a timeout. This retry mechanism is built into Cypress's command queue and event loop, ensuring tests wait for the UI to reach the expected state before failing.
Why designed this way?
Cypress was designed to handle flaky UI tests caused by timing issues. Automatic retries and chaining allow writing tests that are both readable and resilient without manual waits. This design reduces test flakiness and developer frustration, making tests more maintainable and faster to write.
┌───────────────┐
│ cy.get()      │
│ (find element)│
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ .should()     │
│ (assertion 1) │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ .and()        │
│ (assertion 2) │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ Retry loop if │
│ assertion fails│
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does chaining multiple assertions in Cypress run all assertions even if the first one fails? Commit yes or no.
Common Belief:All assertions in a chain run regardless of failures.
Tap to reveal reality
Reality:If an assertion fails, Cypress stops the chain and retries from the start until timeout or success.
Why it matters:Assuming all assertions run can lead to misunderstanding test failures and debugging confusion.
Quick: Can you chain assertions on different elements without separate cy.get() calls? Commit yes or no.
Common Belief:You can chain assertions on different elements directly after one cy.get().
Tap to reveal reality
Reality:Chaining works only on the same subject; different elements require separate cy.get() calls.
Why it matters:Misusing chaining causes tests to fail or check wrong elements, wasting debugging time.
Quick: Does Cypress retry only the failing assertion or the entire chain? Commit your answer.
Common Belief:Cypress retries only the failing assertion in a chain.
Tap to reveal reality
Reality:Cypress retries the entire chain of assertions from the start on failure.
Why it matters:Knowing this helps write efficient assertions and avoid unexpected delays.
Quick: Can you write complex logic inside .should() callbacks? Commit yes or no.
Common Belief:.should() only accepts simple string assertions.
Tap to reveal reality
Reality:.should() can accept functions with multiple custom assertions inside.
Why it matters:Missing this limits test expressiveness and leads to verbose, repetitive code.
Expert Zone
1
Chained assertions share the same subject, so mutations in one assertion affect the next, which can cause subtle bugs if not understood.
2
Using multiple .should() calls versus .should() with .and() can affect readability and retry behavior subtly, influencing test stability.
3
Custom assertion callbacks inside .should() run synchronously but can include multiple expect statements, allowing complex validations in one step.
When NOT to use
Avoid chaining multiple assertions when testing unrelated elements or when assertions require different subjects. Instead, use separate cy.get() calls or custom commands. Also, avoid overly long chains that reduce readability; split complex checks into smaller, named tests.
Production Patterns
In real projects, chaining multiple assertions is used to verify UI elements' state, content, and style in one go, reducing test code size. Teams often create custom commands that internally chain assertions for reusable checks like 'isLoggedIn' or 'formIsValid'. Cypress's retry mechanism with chained assertions helps handle dynamic content and animations gracefully.
Connections
Fluent Interface Pattern
Multiple assertions chaining in Cypress uses the fluent interface pattern to allow readable, chainable commands.
Understanding fluent interfaces in software design helps grasp why chaining assertions feels natural and expressive.
Unit Testing Assertions
Chaining multiple assertions in Cypress builds on the idea of unit test assertions but applies it to UI elements with asynchronous retries.
Knowing basic assertion principles from unit testing clarifies how Cypress extends them for end-to-end testing.
Quality Control in Manufacturing
Chaining multiple assertions is like checking several quality points on a product in one inspection process.
Seeing test assertions as quality checks helps appreciate why grouping them improves efficiency and clarity.
Common Pitfalls
#1Stopping test after first failed assertion without retries
Wrong approach:cy.get('button').should('be.visible').and('have.text', 'Submit') // If 'be.visible' fails, 'have.text' is never checked and test fails immediately without retry
Correct approach:cy.get('button').should('be.visible').and('have.text', 'Submit') // Cypress retries the entire chain until both conditions pass or timeout
Root cause:Misunderstanding Cypress's automatic retry mechanism and how it handles chained assertions.
#2Chaining assertions on different elements without separate queries
Wrong approach:cy.get('button').should('be.visible').and('have.value', 'Click') // 'have.value' applies to button, which may not have a value attribute
Correct approach:cy.get('button').should('be.visible') cy.get('input').should('have.value', 'Click')
Root cause:Confusing subject chaining with multiple element queries.
#3Writing overly long assertion chains reducing readability
Wrong approach:cy.get('form') .should('be.visible') .and('contain.text', 'Login') .and('have.class', 'active') .and('have.length', 1) .and('have.attr', 'data-test', 'login-form') .and('not.be.disabled')
Correct approach:Split into smaller tests or custom commands: cy.get('form').should('be.visible').and('contain.text', 'Login') cy.get('form').should('have.class', 'active').and('have.attr', 'data-test', 'login-form') cy.get('form').should('not.be.disabled')
Root cause:Trying to check too many things at once harms clarity and maintainability.
Key Takeaways
Chaining multiple assertions in Cypress lets you check many conditions on the same element clearly and efficiently.
Use .should() for the first assertion and .and() for additional ones to keep tests readable and grouped logically.
Cypress automatically retries the entire chain of assertions until they pass or timeout, making tests stable against UI delays.
Chaining works only on the same subject; testing different elements requires separate cy.get() calls.
Custom assertion callbacks inside .should() allow complex, flexible checks beyond built-in assertions.

Practice

(1/5)
1. What is the main benefit of chaining multiple assertions with .should() and .and() in Cypress?
easy
A. It allows checking several conditions on the same element in one statement.
B. It runs tests faster by skipping some assertions.
C. It automatically fixes failing tests.
D. It changes the element's properties during the test.

Solution

  1. Step 1: Understand chaining purpose

    Chaining .should() and .and() lets you check multiple things on one element without repeating the element selection.
  2. Step 2: Identify benefit

    This makes tests shorter and clearer by combining checks in one statement.
  3. Final Answer:

    It allows checking several conditions on the same element in one statement. -> Option A
  4. Quick Check:

    Multiple assertions chaining = check many conditions [OK]
Hint: Use .should() once, then .and() for extra checks [OK]
Common Mistakes:
  • Thinking chaining skips assertions
  • Believing chaining changes element state
  • Confusing chaining with test speedup
2. Which of the following is the correct syntax to check that an element is visible and contains text 'Hello' using chaining in Cypress?
easy
A. cy.get('button').should('contain.text', 'Hello').should('be.visible')
B. cy.get('button').and('be.visible').should('contain.text', 'Hello')
C. cy.get('button').should('be.visible').and('contain.text', 'Hello')
D. cy.get('button').and('contain.text', 'Hello').and('be.visible')

Solution

  1. Step 1: Check chaining order

    The first assertion must use .should(), then additional checks use .and().
  2. Step 2: Validate syntax correctness

    cy.get('button').should('be.visible').and('contain.text', 'Hello') correctly uses .should('be.visible') then .and('contain.text', 'Hello'). Options B and D wrongly start with .and(). cy.get('button').should('contain.text', 'Hello').should('be.visible') uses two .should() but chaining prefers .and() after first .should().
  3. Final Answer:

    cy.get('button').should('be.visible').and('contain.text', 'Hello') -> Option C
  4. Quick Check:

    First .should(), then .and() [OK]
Hint: Start with .should(), add checks with .and() [OK]
Common Mistakes:
  • Starting chain with .and() instead of .should()
  • Using multiple .should() instead of .and()
  • Mixing order of assertions incorrectly
3. What will be the result of this Cypress code snippet?
cy.get('#login').should('be.visible').and('have.class', 'active').and('contain.text', 'Login')
medium
A. Test throws syntax error due to chaining.
B. Test fails if element #login is visible but missing class 'active'.
C. Test passes even if element #login is not visible.
D. Test passes if element #login is visible, has class 'active', and contains text 'Login'.

Solution

  1. Step 1: Understand chained assertions

    The code checks three things on element with id 'login': visibility, class presence, and text content.
  2. Step 2: Determine test pass/fail conditions

    All three conditions must be true for the test to pass. If any fail, test fails. Syntax is correct.
  3. Final Answer:

    Test passes if element #login is visible, has class 'active', and contains text 'Login'. -> Option D
  4. Quick Check:

    All chained assertions must pass [OK]
Hint: All chained assertions must be true for pass [OK]
Common Mistakes:
  • Assuming test passes if only some assertions pass
  • Thinking chaining causes syntax error
  • Ignoring visibility check importance
4. Identify the error in this Cypress code snippet:
cy.get('.btn').and('be.enabled').should('have.text', 'Submit')
medium
A. Using .and() before .should() causes a syntax error.
B. The selector '.btn' is invalid.
C. The assertion 'have.text' is not supported.
D. No error; code is correct.

Solution

  1. Step 1: Check chaining order

    Chaining must start with .should(). Using .and() first is invalid syntax.
  2. Step 2: Validate other parts

    Selector '.btn' is valid CSS class selector. 'have.text' is a valid assertion. So no error there.
  3. Final Answer:

    Using .and() before .should() causes a syntax error. -> Option A
  4. Quick Check:

    Start chain with .should(), not .and() [OK]
Hint: Always start assertions with .should() [OK]
Common Mistakes:
  • Starting chain with .and()
  • Confusing selector syntax
  • Misunderstanding assertion names
5. You want to verify a form input with id 'email' is visible, enabled, has placeholder 'Enter email', and is empty. Which chained assertions correctly achieve this?
hard
A. cy.get('#email').should('be.enabled').should('be.visible').should('have.attr', 'placeholder', 'Enter email').should('have.value', '')
B. cy.get('#email').should('be.visible').and('be.enabled').and('have.attr', 'placeholder', 'Enter email').and('have.value', '')
C. cy.get('#email').and('be.visible').and('be.enabled').and('have.attr', 'placeholder', 'Enter email').and('have.value', '')
D. cy.get('#email').should('have.value', '').should('be.visible').should('be.enabled').should('have.attr', 'placeholder', 'Enter email')

Solution

  1. Step 1: Confirm chaining syntax

    Chaining must start with .should() and continue with .and() for multiple assertions on the same element.
  2. Step 2: Check assertion order and correctness

    cy.get('#email').should('be.visible').and('be.enabled').and('have.attr', 'placeholder', 'Enter email').and('have.value', '') starts with .should('be.visible') then chains .and() for other checks. cy.get('#email').should('have.value', '').should('be.visible').should('be.enabled').should('have.attr', 'placeholder', 'Enter email') uses multiple .should() starting with value check, which is allowed but less concise and best practice is to start with visibility. cy.get('#email').and('be.visible').and('be.enabled').and('have.attr', 'placeholder', 'Enter email').and('have.value', '') incorrectly starts with .and(). cy.get('#email').should('be.enabled').should('be.visible').should('have.attr', 'placeholder', 'Enter email').should('have.value', '') uses multiple .should() which is allowed but less concise.
  3. Final Answer:

    cy.get('#email').should('be.visible').and('be.enabled').and('have.attr', 'placeholder', 'Enter email').and('have.value', '') -> Option B
  4. Quick Check:

    Start with .should(), chain with .and() for clarity [OK]
Hint: Start with .should('be.visible'), then chain .and() [OK]
Common Mistakes:
  • Starting chain with .and()
  • Using multiple .should() instead of chaining
  • Ignoring assertion order for readability
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard