Cypress commands are asynchronous and chained — you cannot store a cy.get() result in a variable the way you store a find_element() result in Selenium. So how do you reference an element, an intercepted response, or a computed value later in your test? The answer is aliases. Cypress’s .as() command saves a reference that you can retrieve with cy.get('@aliasName') or access in hooks and assertions.
Aliases — Referencing Elements, Routes and Values
Aliases solve the “I need this value later” problem without breaking the Cypress async command model.
// ── Element aliases ──
// Save an element reference for reuse
cy.get('[data-cy="product-list"]').as('productList');
// Use the alias later in the same test
cy.get('@productList').find('.product-card').should('have.length', 6);
cy.get('@productList').find('.product-card').first().click();
// ── Intercept aliases (network stubs) ──
// Alias an intercepted network request
cy.intercept('GET', '/api/products').as('getProducts');
// Trigger the request
cy.visit('/shop');
// Wait for the aliased request to complete
cy.wait('@getProducts').its('response.statusCode').should('eq', 200);
// Access the response body
cy.wait('@getProducts').then((interception) => {
expect(interception.response.body).to.have.length(6);
expect(interception.response.body[0]).to.have.property('name');
});
// ── Value aliases with cy.wrap() ──
// Wrap a JavaScript value so it becomes a Cypress chainable
const today = new Date().toISOString().split('T')[0];
cy.wrap(today).as('todayDate');
// Use the wrapped value later
cy.get('@todayDate').then((date) => {
cy.get('#date-field').should('have.value', date);
});
// ── Extracting and reusing dynamic values ──
// Save a dynamically generated order ID for later verification
cy.get('[data-cy="order-id"]')
.invoke('text')
.as('orderId');
// Use the saved order ID in a subsequent step
cy.get('@orderId').then((orderId) => {
cy.visit(`/orders/${orderId}`);
cy.get('h1').should('contain.text', orderId);
});
// ── Aliases in hooks (beforeEach / afterEach) ──
describe('Shopping Cart', () => {
beforeEach(() => {
cy.intercept('GET', '/api/products').as('products');
cy.intercept('POST', '/api/cart').as('addToCart');
cy.visit('/shop');
cy.wait('@products');
});
it('should add product to cart', () => {
cy.get('.product-card').first().find('.add-btn').click();
cy.wait('@addToCart')
.its('response.statusCode')
.should('eq', 201);
});
});
// ── cy.wrap() — making non-Cypress values chainable ──
// Wrap a plain object
cy.wrap({ name: 'Alice', role: 'admin' })
.its('name')
.should('eq', 'Alice');
// Wrap a promise
cy.wrap(fetch('/api/health').then(r => r.json()))
.its('status')
.should('eq', 'ok');
// Wrap an array for iteration
cy.wrap([1, 2, 3, 4, 5]).each((num) => {
expect(num).to.be.lessThan(10);
});
.as() are scoped to the current test. They are automatically cleaned up between tests — you cannot reference an alias created in one it() block from another. Aliases created in beforeEach() are available in every test within that describe() block because beforeEach() runs before each test. This scoping prevents state leakage between tests and aligns with the principle of test independence.cy.intercept().as('apiCall') combined with cy.wait('@apiCall') as your primary synchronisation strategy for pages that load data via API calls. Instead of waiting for DOM elements to appear (which is indirect), wait for the API response that provides the data (which is direct). This approach is both faster and more deterministic: you know the data has arrived, so the DOM update is guaranteed to follow immediately.let savedId; in the describe block, assigned in it('creates order') and used in it('verifies order'). This creates test interdependence — if the creation test fails or is skipped, the verification test fails too. Each test should set up its own data independently.Common Mistakes
Mistake 1 — Assigning cy.get() to a variable instead of using an alias
❌ Wrong: const btn = cy.get('.submit'); btn.click(); — Cypress commands are not assignable to variables.
✅ Correct: cy.get('.submit').as('submitBtn'); cy.get('@submitBtn').click(); — or simply chain: cy.get('.submit').click();
Mistake 2 — Not waiting for aliased intercepts before asserting on the page
❌ Wrong: cy.intercept('GET', '/api/data').as('data'); cy.visit('/page'); cy.get('.results').should('have.length', 5); — the assertion may fire before the API response arrives.
✅ Correct: cy.intercept(...).as('data'); cy.visit('/page'); cy.wait('@data'); cy.get('.results').should('have.length', 5); — wait for the data, then assert.