📣Join us June 10th @ 2PM EDT/11 AM PDT to learn how Optimizely tests features and feature flags 300% faster with Cypress

Register

Debugging

Improve this doc

What you’ll learn

  • How Cypress runs in the same event loop with your code, keeping debugging less demanding and more understandable
  • How Cypress embraces the standard Developer Tools
  • How and when to use debugger and the shorthand .debug() command

Using debugger

Your Cypress test code runs in the same run loop as your application. This means you have access to the code running on the page, as well as the things the browser makes available to you, like document, window, and debugger.

Debug just like you always do

Based on those statements, you might be tempted to throw a debugger into your test, like so:

it('let me debug like a fiend', () => {
  cy.visit('/my/page/path')

  cy.get('.selector-in-question')

  debugger // Doesn't work
})

This may not work exactly as you are expecting. As you may remember from the Introduction to Cypress, cy commands enqueue an action to be taken later. Can you see what the test above will do given that perspective?

Both cy.visit() and cy.get() will return immediately, having enqueued their work to be done later, and debugger will be executed before any of the commands have actually run.

Let’s use .then() to tap into the Cypress command during execution and add a debugger at the appropriate time:

it('let me debug when the after the command executes', () => {
  cy.visit('/my/page/path')

  cy.get('.selector-in-question')
    .then(($selectedElement) => {
      // Debugger is hit after the cy.visit
      // and cy.get command have completed
      debugger
    })
})

Now we’re in business! The first time through, cy.visit() and the cy.get() chain (with its .then() attached) are enqueued for Cypress to execute. The it block exits, and Cypress starts its work:

  1. The page is visited, and Cypress waits for it to load.
  2. The element is queried, and Cypress automatically waits and retries for a few moments if it isn’t found immediately.
  3. The function passed to .then() is executed, with the found element yielded to it.
  4. Within the context of the .then() function, the debugger is called, halting the browser and calling focus to the Developer Tools.
  5. You’re in! Inspect the state of your application like you normally would if you’d dropped the debugger into your application code.

Using .debug()

Cypress also exposes a shortcut for debugging commands, .debug(). Let’s rewrite the test above using this helper method:

it('let me debug like a fiend', () => {
  cy.visit('/my/page/path')

  cy.get('.selector-in-question')
    .debug()
})

The current subject that is yielded by the cy.get() is exposed as the variable subject within your Developer Tools so that you can interact with it in the console.

Debugging Subject

Use .debug() to quickly inspect any (or many!) part(s) of your application during the test. You can attach it to any Cypress chain of commands to have a look at the system’s state at that moment.

Step through test commands

You can run the test command by command using the .pause() command.

it('adds items', () => {
  cy.pause()
  cy.get('.new-todo')
  // more commands
})

This allows you to inspect the web application, the DOM, the network, and any storage after each command to make sure everything happens as expected.

Using the Developer Tools

Though Cypress has built out an excellent Test Runner to help you understand what is happening in your application and your tests, there’s no replacing all the amazing work browser teams have done on their built-in development tools. Once again, we see that Cypress goes with the flow of the modern ecosystem, opting to leverage these tools wherever possible.

See it in action!

You can see a walk-through of debugging some application code from Cypress in this segment from our React tutorial series.

Get console logs for commands

All of Cypress’s commands, when clicked on within the Command Log, print extra information about the command, its subject, and its yielded result. Try clicking around the Command Log with your Developer Tools open! You may find some useful information here.

When clicking on .type() command, the Developer Tools console outputs the following:

Console Log type

Errors

Sometimes tests fail. Sometimes we want them to fail, just so we know they’re testing the right thing when they pass. But other times, tests fail unintentionally and we need to figure out why. Cypress provides some tools to help make that process as easy as possible.

Anatomy of an error

Let’s take a look at the anatomy of an error and how it is displayed in Cypress, by way of a failing test.

it('reroutes on users page', () => {
  cy.contains('Users').click()
  cy.url().should('include', 'users')
})

The center of the <li>Users</li> element is hidden from view in our application under test, so the test above will fail. Within Cypress, an error will show on failure that includes the following pieces of information:

  1. Error name: This is the type of the error (e.g. AssertionError, CypressError)
  2. Error message: This generally tells you what went wrong. It can vary in length. Some are short like in the example, while some are long, and may tell you exactly how to fix the error. Some also contain a Learn more link that will take you to relevant Cypress documentation.
  3. Learn more: Some error messages contain a Learn more link that will take you to relevant Cypress documentation.
  4. View stack trace: Clicking this toggles the visibility of the stack trace. Stack traces vary in length. Clicking on a blue file path will open the file in your preferred file opener.
  5. Code frame file: This is usually the top line of the stack trace and it shows the file, line number, and column number that is highlighted in the code frame below. Clicking on this link will open the file in your preferred file opener and highlight the line and column in editors that support it.
  6. Code frame: This shows a snippet of code where the failure occurred, with the relevant line and column highlighted.
  7. Print to console button: Click this to print the full error to your DevTools console. This will usually allow you to click on lines in the stack trace and open files in your DevTools.

example command failure error

Source maps

Cypress utilizes source maps to enhance the error experience. Stack traces are translated so that your source files are shown instead of the generated file that is loaded by the browser. This also enables displaying code frames. Without inline source maps, you will not see code frames.

By default, Cypress will include an inline source map in your spec file, so you will get the most out of the error experience. If you modify the preprocessor, ensure that inline source maps are enabled to get the same experience. With webpack and the webpack preprocessor, for example, set the devtool option to inline-source-map.

Log Cypress events

Cypress emits multiple events you can listen to as shown below. Read more about logging events in the browser here.

console log events for debugging

Run Cypress command outside the test

If you need to run a Cypress command straight from the Developer Tools console, you can use the internal command cy.now('command name', ...arguments). For example, to run the equivalent of cy.task('database', 123) outside the normal execution command chain:

cy.now('task', 123)
  .then(console.log)
// runs cy.task(123) and prints the resolved value

The cy.now() command is an internal command and may change in the future.

Cypress fiddle

While learning Cypress it may be a good idea to try small tests against some HTML. We have written a @cypress/fiddle plugin for this. It can quickly mount any given HTML and run some Cypress test commands against it.

Troubleshooting Cypress

There are times when you will encounter errors or unexpected behavior with Cypress itself. In this situation, we recommend checking our out Troubleshooting Guide.