A Beginner‘s Guide to Extracting Attribute Values with Cypress

As a seasoned quality assurance architect with over 15 years of expertise testing complex web and mobile applications, I have a deep appreciation for test automation. In the early 2000s when I began my career, we would spend days repeating manual test cases which was mundane and error-prone. The advent of test automation was a boon, allowing us to replicate user workflows programmatically. However, those early frameworks had severe limitations around selector stability and maintenance.

I distinctly remember the first time I used Cypress a few years ago. The text-based syntax, with commands like .get() and .click(), made so much intuitive sense! The framework was built ground-up with the pain points of test authors in mind. Over time, Cypress has become my automation tool of choice for its unparalleled selector resilience in the face of DOM changes.

In this article, I will explain the concepts around Cypress selecting elements on the page using .get() and retrieving their attribute values programmatically. By the end, you will be able to unlock the power of attributes to create conditional test flows catered to your unique needs.

Overview of Attributes and Properties

As Cypress interacts with web page elements, it is important to understand attributes vs. properties.

Attributes are defined in the HTML markup. They provide additional information about an element like id, class names, data field etc:

<input type="text" class="form-control" disabled>

Here type, class etc. are attributes of the input tag.

Properties refer to the runtime characteristics such as value, checked status etc. These can change based on user interaction:

//After user types 
input.value = "Name" 

//After user checks
checkbox.checked = true

Attributes and properties can often be correlated, for example:

<input type="checkbox" checked>

The checked attribute sets the initial checked property to true.

With this context, let‘s understand how Cypress allows interacting with attributes and properties.

Cypress Selectors Refresher

Cypress offers a rich set of selectors to target elements uniquely like id, class, tag, attributes etc. For example:

cy.get(‘#email-input‘) // Element with id as email-input
cy.get(‘.form-control‘) // Elements having form-control class  

Selectors can also target attributes directly:

cy.get(‘[disabled]‘) // Get all disabled elements

Chaining cy.get() calls helps drilling into the DOM:

cy.get(‘form‘).within(form => {
  cy.get(‘input‘).focus() 
})

I recommend this guide to get familiar with the different selector types.

Checking for Attribute Presence

The most basic attribute check is to assert its existence. For example, to validate that the submit button has a type:

cy.get(‘#submitBtn‘).should(‘have.attr‘, ‘type‘)

This uses the .should() assertion chainer to confirm attribute presence. Some other useful assertions are:

  • not.have.attr() – Assert absence
  • have.class() – Validate CSS classes
  • have.id() – Confirm presence of id

These provide quick sanity checks without needing actual values.

Retrieving Attribute Values

The .get() command yields the DOM element it selects. We can invoke jQuery‘s attr() on this element to retrieve attribute values:

cy.get(‘#email‘).invoke(‘attr‘, ‘type‘)
  .should(‘equal‘, ‘email‘) 
  • .get() first yields the DOM element
  • .invoke() runs attr() to extract the attribute value
  • Chain assertions to validate values

Another way is to use .then() to get the yielded element and destructure the attributes object:

cy.get(‘#submitBtn‘).then(button => {

  const { type, disabled } = button[0]

  expect(type).to.equal(‘submit‘)
  expect(disabled).to.be.false

})

This allows cleanly extracting multiple values at once.

Reading Property Values

Along with attributes, Cypress allows verifying property values that may change dynamically:

cy.get(‘#nameInput‘) 
  .invoke(‘prop‘, ‘value‘)
  .should(‘equal‘, ‘John‘)  

We use .invoke(‘prop‘) instead of attr() to get property values. Some other helpful properties are:

  • innerText – Visible text content
  • checked – Status of checkbox/radio
  • selected – Whether dropdown option is selected

So in summary:

  • Attributes – Information present in HTML code
  • Properties – Runtime characteristic reflecting state
Description Attributes Properties
Human-readable Yes Limited
Changed by user interaction No Yes
Easy to query .invoke(‘attr‘) .invoke(‘prop‘)

With this foundation, let‘s look at some practical examples of using attribute and property values in test automation.

Using Attribute Values

Extracting attributes unlocks several testing use cases:

Dynamic selectors

Many web frameworks like React and Vue generate random ID attributes. Instead of brittle selectors like .get(‘#id1‘), use data attributes:

<button data-test-id="submit">Submit</button>

cy.get(‘[data-test-id="submit"]‘).click() 

Conditional testing

Attribute values can drive conditional test logic based on page state:

cy.get(‘.banner‘).invoke(‘attr‘, ‘data-type‘)
  .then(type => {
    if(type === ‘warning‘) {
      //do something
    }
    else if(type === ‘error‘) {
     //do something else
    }
})

This allows dynamic test flows instead of separate tests for each scenario.

Visual testing

Some attributes directly relate to styling. Asserting them validates the visual rendering:

cy.get(‘img‘).should(‘have.class‘, ‘large-image‘)

We indirectly checked image dimensions by asserting CSS classes.

Hidden element testing

In some cases, we may need to verify text content that is visually hidden from users:

<div style="display:none">
  Lorem ipsum dolor sit amet
</div>

The innerText property allows asserting this content:

cy.get(‘div‘).invoke(‘prop‘, ‘innerText‘)
  .should(‘equal‘, ‘Lorem ipsum dolor sit amet‘) 

There are also other edge cases where properties provide valuable insights into an element‘s state.

Dealing with Dynamic Values

Modern web frameworks render content dynamically via client-side JavaScript. This often generates unpredictable ID/class attributes.

Instead of brittle selectors tied to UI attributes, introduce durable data-* attributes:

<button id="xyz123" class="btn-456" 
        data-test="submit-button">
  Submit
</button>

cy.get(‘[data-test="submit-button"]‘)  

The fictional data-test isolates tests from automatically generated ID/classes. Some tips on handling dynamic attributes:

  • *Use data- attrs**: Help create customizable selectors
  • Avoid classes: Auto-generated to ensure uniqueness
  • Combine attributes: Layer ID, type, name etc for robustness

With careful planning, unpredictability can be tamed and selectors can remain resilient.

Common Tips and Tricks

Here are some handy techniques I have learned over the years:

  • Create reusable selector variables for repetitive usage
  • Assert data types using .should(‘be.a‘, ‘string‘)
  • Use .should() instead of .then() so assertions auto-retry on failure
  • Pass { timeout: 5000} to apply custom timeout for conditional retries
  • Always start testing simply and increment scope gradually

These tips will ensure systematic test development and stable test suites.

Conclusion

I hope this guide provided you a foundation on using Cypress to interact with element attributes and extract their values. With practice, you will be able to create dynamic conditional flows powered by attribute data. The .get() command unlocks immense potential which goes far beyond just selecting elements on the page.

Please feel free to reach out if you have any other questions. Happy testing!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.