Introduction to Cypress App – A Beginner’s Guide to Testing
Introduction
Cypress is a powerful and user-friendly testing framework designed for modern web applications. This guide will introduce you to Cypress, covering essential concepts, querying elements, handling asynchronous commands, and best practices for writing stable tests.
What You’ll Learn
- Cypress rules for efficient testing
- How to query and interact with elements
- Understanding Cypress’ asynchronous nature
- Making reliable assertions in tests
Why Use Cypress?
Cypress simplifies test automation by providing:
- ✔️ Easy setup – No additional dependencies required
- ✔️ Real-time execution – Tests run directly in the browser
- ✔️ Automatic waits – No need for manual
wait
statements - ✔️ Readable syntax – Intuitive commands for better test writing
Writing Your First Cypress Test
Here’s an example end-to-end test for creating a new post:
describe('Post Resource', () => {
it('Creating a New Post', () => {
cy.visit('/posts/new');
cy.get("input.post-title").type("My First Post");
cy.get("input.post-body").type("Hello, world!");
cy.contains("Submit").click();
cy.get("h1").should("contain", "My First Post");
});
});
How This Works:
- Visits
/posts/new
- Fills out the title and content
- Clicks the “Submit” button
- Confirms that the post appears correctly
Querying Elements in Cypress
Cypress uses jQuery-like selectors to find elements:
cy.get('.my-selector') // Select by class
cy.get('#element-id') // Select by ID
You can also query by text content for a more user-friendly approach:
cy.contains('New Post') // Finds an element containing the text "New Post"
cy.get('.main').contains('Submit') // Finds the "Submit" button within .main
Best Practice: Avoid selecting elements based on dynamically generated classes or styles.
Handling Missing Elements
Unlike jQuery, Cypress automatically retries queries until:
- The element appears
- The timeout is reached
cy.get('#element', { timeout: 10000 }); // Waits up to 10 seconds
This reduces flakiness in tests by handling:
- ✅ Slow-loading DOM elements
- ✅ Delayed API responses
- ✅ Framework rendering delays
Handling Asynchronous Commands
Cypress commands do not return elements synchronously like jQuery. Instead, they use promises:
❌ This won’t work:
const $cyElement = cy.get('.element'); // Cypress commands are async!
✅ Use .then()
to handle async behavior:
cy.get('.element').then(($el) => {
doSomething($el);
});
Cypress waits for elements to appear and ensures stability before executing further commands.
Customizing Timeouts for Stability
Cypress allows global and per-command timeouts to handle slow elements:
🔹 Per-command timeout:
cy.get('.slow-element', { timeout: 8000 });
🔹 Global timeout setting:
Cypress.config('defaultCommandTimeout', 10000);
Tip: Avoid setting unnecessarily long timeouts, as it can slow down your test execution.
Chaining Commands in Cypress
Understanding Cypress Command Chains
Cypress manages a Promise chain on your behalf, allowing seamless chaining of commands. Each command yields a ‘subject’ to the next command in the chain. While developers do not need to work with Promises explicitly, understanding them is beneficial.
Interacting With Elements
Cypress enables interaction with page elements using action commands like .click()
and .type()
. These commands are chained with cy.get()
or cy.contains()
. For example:
cy.get('textarea.post-body').type('This is an excellent post.')
Here, .type()
acts on the element returned by cy.get()
.
Other useful action commands include:
.blur()
– Makes a focused element lose focus..focus()
– Focuses on a DOM element..clear()
– Clears an input or textarea field..check()
– Selects checkbox(es) or radio buttons..uncheck()
– Deselects checkbox(es)..select()
– Selects an<option>
within a<select>
..dblclick()
– Performs a double-click..rightclick()
– Performs a right-click.
Cypress ensures elements are in an “actionable” state before interacting with them:
- Not hidden
- Not covered
- Not disabled
- Not animating
Asserting About Elements
Assertions allow verification of element states:
cy.get(':checkbox').should('be.disabled')
cy.get('form').should('have.class', 'form-horizontal')
cy.get('input').should('not.have.value', 'US')
Cypress automatically waits until these assertions pass.
Subject Management
Each Cypress command yields a subject for the next command. Examples:
cy.clearCookies() // Yields null
cy.get('.main-container').contains('Headlines').click()
.click()
requires a DOM element..contains()
searches within the previous subject unless chained directly fromcy
.
Using .then()
to Act on a Subject
Use .then()
to manipulate subjects within a chain:
cy.get('#some-link').then(($el) => {
const href = $el.prop('href').replace(/(#.*)/, '')
return href
}).then((href) => {
// Use modified href
})
Using Aliases for Reusability
Aliases store references for future use:
cy.get('.my-selector').as('myElement')
cy.get('@myElement').click()
This avoids stale elements and ensures elements are re-queried correctly.
Commands Are Asynchronous
Cypress commands do not execute immediately but are queued for execution. For example:
it('hides the thing when it is clicked', () => {
cy.visit('/my/resource/path')
cy.get(".hides-when-clicked").should("be.visible").click()
cy.get('.hides-when-clicked').should('not.be.visible')
})
The test function queues commands, and Cypress executes them sequentially after the function exits.
Handling Async and Sync Code
Incorrect:
it('does not work as expected', () => {
cy.visit('/my/resource/path')
cy.get('.awesome-selector').click()
let el = Cypress.$('.new-el') // Evaluates too soon
if (el.length) {
cy.get('.another-selector')
} else {
cy.get('.optional-selector')
}
})
Correct usage with .then()
:
it('works correctly', () => {
cy.visit('/my/resource/path')
cy.get('.awesome-selector').click().then(() => {
let el = Cypress.$('.new-el')
if (el.length) {
cy.get('.another-selector')
} else {
cy.get('.optional-selector')
}
})
})
By using .then()
, Cypress ensures the code runs only after previous commands complete execution.
Understanding Cypress’ command chaining, subject management, and handling of asynchronous execution is crucial for writing stable and readable tests.
h1>Cypress Assertions
What Are Assertions?
Assertions describe the expected state of elements, objects, and applications in a test. Cypress automatically retries assertions until they pass or timeout, making them more reliable compared to other testing tools.
Example of Assertions in English:
“After clicking this button, I expect it to have an active
class.”
Cypress Equivalent:
cy.get('button').click()
cy.get('button').should('have.class', 'active')
Even if the .active
class is applied asynchronously, Cypress will wait and retry until the assertion passes.
Example: HTTP Request Assertion
“After making an API call, I expect the response to contain {name: 'Jane'}
.”
Cypress Equivalent:
cy.request('/users/1').its('body').should('deep.eq', { name: 'Jane' })
When to Use Assertions?
Sometimes, explicit assertions are unnecessary. Consider the test below:
cy.visit('/home')
cy.get('.main-menu').contains('New Project').click()
cy.get('.title').type('My Awesome Project')
cy.get('form').submit()
Without explicit assertions, Cypress still verifies multiple conditions:
- The page loads successfully.
- Elements exist in the DOM.
- Actions like
.click()
and.type()
work as expected. - The form submission doesn’t fail.
Implicit Assertions
Many Cypress commands have built-in assertions:
cy.visit()
expects a 200 status response.cy.request()
expects a valid response from the server.cy.contains()
waits for the element to exist.cy.get()
,.find()
,.type()
,.click()
, and.its()
all have automatic retries.
Example: Existence and Actionability
cy.get('button').click()
Cypress ensures:
- The button exists in the DOM.
- The button is actionable (not disabled or hidden) before clicking.
Example: Waiting for an Element to Disappear
cy.get('button.close').click()
cy.get('button.close').should('not.exist')
cy.get('#modal').should('not.exist')
Adding .should('not.exist')
ensures Cypress does not wait for the element to appear but instead waits for it to be removed.
Writing Assertions
There are two main ways to write assertions in Cypress:
1. Using Cypress Commands (.should()
and .and()
)
cy.get('tbody tr:first').should('have.class', 'active')
You can chain multiple assertions:
cy.get('#header a')
.should('have.class', 'active')
.and('have.attr', 'href', '/users')
2. Using Mocha Assertions (expect
)
Useful for custom logic before asserting:
expect(true).to.be.true
Example: Complex Assertions
cy.get('p').should(($p) => {
let texts = $p.map((i, el) => Cypress.$(el).text()).get()
expect(texts).to.have.length(3)
expect(texts).to.deep.eq([
'Some text from first p',
'More text from second p',
'And even more text from third p',
])
})
Caution: Ensure .should()
Callbacks Are Idempotent
Cypress retries .should()
callbacks, so avoid functions that cause side effects.
Handling Timeouts
All assertions share the same timeout settings. Cypress automatically waits for assertions to pass before failing.
Example: Default Timeout
cy.get('.mobile-nav')
Cypress waits up to 4 seconds for .mobile-nav
to appear.
Example: Custom Timeout
cy.get('.mobile-nav', { timeout: 10000 }).should('be.visible').and('contain', 'Home')
This extends the timeout to 10 seconds.
Summary
- Cypress assertions retry automatically.
- Implicit assertions exist in many Cypress commands.
.should()
and.and()
are preferred for inline assertions.- Use
expect
for more complex logic. - Cypress manages timeouts automatically, but they can be customized.
By understanding how assertions work in Cypress, you can write more stable, reliable tests.
Leave a Reply