Resolve the "function is not a function" error in Cypress by ensuring you are correctly chaining commands within your test suite. This issue typically arises when a command returns undefined or when asynchronous operations are mishandled, causing subsequent method calls to fail on non-function values.
Understanding the Error Context
The "function is not a function" error is a classic JavaScript runtime exception that manifests frequently in Cypress automation scripts. Unlike standard Node.js applications where errors might be caught gracefully, Cypress fails fast during test execution, halting the entire spec file immediately. This behavior is designed to prevent false positives, but it can be confusing for developers who expect the test to continue despite a minor assertion failure.
Cypress commands are asynchronous and return a special object called a Chainable. When you invoke a command like cypress.get() or cypress.click(), you are not getting the DOM element directly; you are getting a wrapper that holds the result. If you attempt to call a method on this wrapper incorrectly, or if the previous command in the chain resolves to something unexpected, the JavaScript engine throws this error because it expects a callable function but receives an object or primitive value instead.
Common Causes and Diagnostic Steps
Identifying the root cause requires examining the specific line of code where the error occurs. The most frequent culprits involve improper use of jQuery-like methods versus Cypress-specific commands, or incorrect handling of promises.
- Incorrect Command Chaining: A common mistake is mixing native JavaScript array methods with Cypress commands. For example, calling
.map()on a Cypress chain without understanding that it returns a new Chainable rather than a standard array can lead to type mismatches later in the pipeline. - Missing Return Statements: In custom commands defined in
cypress/support/commands.js, failing to explicitly return the result of a Cypress command breaks the chain. Without the return statement, the next command in the sequence receivesundefined, leading to the error when it tries to invoke a method on it. - Asynchronous Timing Issues: Attempting to access properties of an element before it has been fully rendered or available in the DOM can result in retrieving
nullorundefined. If a subsequent step assumes this property exists and is a function, the test will crash.
To diagnose these issues, enable verbose logging by running your tests with the --debug flag. This provides a detailed output of each command execution and its resolved value, allowing you to pinpoint exactly which variable holds the unexpected type.
Fixing Incorrect Command Usage
One of the primary solutions involves correcting how you interact with DOM elements. Ensure that you are using Cypress's built-in commands such as cy.get(), cy.contains(), and cy.find() exclusively for DOM traversal. Avoid using native jQuery methods like $().find() inside Cypress tests unless absolutely necessary, as they do not integrate with Cypress's automatic retry-ability and waiting mechanisms.
// Incorrect: Mixing jQuery with Cypress chains
cy.get('.button').then(($btn) => {
$btn.find('span').click(); // This may break the chain context
});
// Correct: Using Cypress commands throughout
cy.get('.button')
.find('span')
.click();
In the correct example above, cy.get() returns a Chainable, and .find() is a valid method on that Chainable. This maintains the integrity of the promise chain and ensures that each step waits for the previous one to complete successfully.
Properly Handling Custom Commands
If you have created custom commands in your support file, verify that every command returns the appropriate Cypress object. A typical custom command should look like this:
Cypress.Commands.add('login', (username, password) => {
cy.visit('/login');
cy.get('#username').type(username);
cy.get('#password').type(password);
cy.get('form').submit();
// Crucial: Return the result to maintain chainability
return cy.url();
});
By returning cy.url(), the caller can chain further assertions like .should('include', '/dashboard'). Omitting the return statement causes the caller to receive undefined, triggering the error when it attempts to call .should().
Managing Asynchronous Operations
When dealing with external APIs or complex asynchronous logic, avoid using async/await directly inside Cypress test cases unless you are careful about how results are passed back into the Cypress chain. Instead, use cy.then() to handle raw JavaScript logic within the Cypress queue.
cy.request('GET', '/api/data')
.then((response) => {
const data = response.body;
// Process data here
return data.items;
})
.then((items) => {
items.forEach(item => {
cy.log(item.name);
});
});
This approach keeps all operations within the Cypress event loop, ensuring that retries and timeouts are managed correctly by the framework.
Frequently Asked Questions
Why does my test pass locally but fail in CI?
Differences in browser versions, network speeds, or system resources between local and CI environments can affect timing. Ensure your tests include explicit waits or rely on Cypress's default retry-ability rather than hardcoded delays like cy.wait(5000).
How do I debug a specific line throwing this error?
Use the Cypress Test Runner's interactive mode and add cy.pause() before the failing line. This allows you to inspect the current state of the application and manually execute commands in the console to see what values are being returned.
Can I ignore this error and continue the test?
No, this is a fatal JavaScript error that stops script execution. You must fix the underlying code logic. Ignoring it is not possible without wrapping the code in a try-catch block, which is generally discouraged in Cypress as it masks failures.
Is this error related to network connectivity issues?
While network issues can cause requests to fail, they typically result in different error messages like "request failed" or "timeout." The "function is not a function" error is strictly a code logic issue regarding object types and method availability.
What is the difference between cy.then() and .then()?
cy.then() is a Cypress command that yields the previous subject and executes a callback within the Cypress queue. Native .then() is a Promise method that runs outside the Cypress queue, which can lead to race conditions and loss of automatic retries.