Cypress supports implicit assertions with .should() and .and() (which retry) and explicit assertions with expect()/assert (BDD/TDD styles from Chai). Implicit assertions are preferred because they retry automatically.
.should('have.text', 'Hi') and chained .and('be.visible') attach to a command and retry until they pass — ideal for async UIs. expect(value).to.equal(5) is a one-time synchronous assertion, used inside .then() when you have a concrete value. Cypress bundles Chai, Chai-jQuery, and Sinon-Chai, giving assertions like should('have.class'), should('contain'), should('have.length'), and should('have.been.called') for spies. You can also pass a callback to should for complex checks: .should(($el) => { expect($el).to.have.length(3); }), which retries the whole callback.
Verifying a list rendered from an API: cy.get('[data-cy=row]').should('have.length', 10).and('contain', 'Active') — Cypress retries until all 10 rows render, avoiding a race with the network.
A test uses cy.get('.count').then($el => expect($el.text()).to.equal('5')) and is flaky. How do you make it stable?