Attributes Vs Properties

Sometimes I use cy.get('...').should('have.attr', '...') and sometimes I use cy.get('...').should('have.prop', '...'). Similarly, sometimes I invoke the jQuery method attropen in new window via cy.get('...').invoke('attr', '...') and sometimes I invoke the propopen in new window method like cy.get('...').invoke('prop', '...').

What is the difference?

📺 Watch this recipe explained in the video Attributes Vs Propertiesopen in new window.

An HTML element on the page has attributes which are always string values:

<input id="example" type="checkbox" checked="checked" />

The above element has the following HTML attributes: id, type, and checked. We can confirm each declared attribute using the have.attr assertion.

cy.get('#example')
  .should('have.attr', 'id', 'example')
  .and('have.attr', 'type', 'checkbox')
  .and('have.attr', 'checked', 'checked')

We could get the attribute string value by calling the jQuery attr method. Since it yields the attribute value, we need to query the DOM again if we need to confirm more attributes.

cy.log('**invoke attr method**')
cy.get('#example')
  .invoke('attr', 'id')
  .should('equal', 'example')
cy.get('#example')
  .invoke('attr', 'type')
  .should('equal', 'checkbox')
cy.get('#example')
  .invoke('attr', 'checked')
  .should('equal', 'checked')

Tip: assertion have.attr with just the name of the attribute yields the value of the attribute. The above code could be rewritten as

cy.log('**have.attr assertion**')
cy.get('#example')
  .should('have.attr', 'id')
  .should('equal', 'example')
cy.get('#example')
  .should('have.attr', 'type')
  .should('equal', 'checkbox')
cy.get('#example')
  .should('have.attr', 'checked')
  .should('equal', 'checked')

The browser reads the HTML and converts each declared HTML element into an object. This object has links to its parent, any children, and lots of other DOM properties. Let's see their names (most of them come from the prototype chain)

DOM element properties

The properties have everything needed by the browser at runtime. For example, the HTML attribute checked="checked" becomes the Boolean property checked

Checked element property

To retrieve the runtime element properties I use the jQuery prop method and Chai-Jquery assertion have.prop. For example, we can check if the input checkbox element is currently checked:

cy.get('#example').should('have.prop', 'checked', true)
// equivalent
cy.get('#example').invoke('prop', 'checked').should('be.true')

Tip: I personally like the have.prop assertion since it shows the property name in the Command Log, while the cy.invoke does not.

The Command Log difference between the prop assertion and prop call

See also