Cypress Stubbing – What is it?
By Gopal Kaushal, Community Contributor - December 24, 2024
Testing is integral to modern web development because it ensures applications behave as expected. Due to its speed, reliability, and usability, Cypress has become a favorite for developers. Cypress’s feature to test via stubbing allows you to simulate certain behaviors within your test without relying on external services or components.
This article delves deeper into what stubbing is in Cypress and how we can use the cy.stub() method.
- What is Stubbing?
- What is the Cypress Stub() Method?
- Why is the Cypress Stub() Method used?
- Ways to do Stubbing in Cypress
- 1. Basic Usage of cy.stub()
- 2. Stubbing with cy.intercept()
- 3. Stubbing with cy.spy()
What is Stubbing?
Stubbing is a testing technique for replacing certain functions or methods with versions that return predefined values.
This technique makes isolating the unit under test easier from any external dependencies, like an API, database, or third-party service. This way, it’s more controlled and consistent. As a result, faster tests are executed. With “stubs” to calls outside your code, the developers are better prepared and can focus on the core application functionality, assuming no variability of those network requests, other components, etc.
While stubbing is useful when external data or functions are not currently available, it is generally recommended to use stubbing as little as possible because it uses unrealistic data or functionalities.
What is the Cypress Stub() Method?
Cypress provides the cy.stub() method as a built-in tool to create function stubs. This method allows you to replace an existing function with a stub, enabling you to control the function’s behavior during the test. The cy.stub() method is often used when you want to simulate certain behaviors, such as returning specific data or tracking how many times a function is called.
The cy.stub() method is typically used to replace a function or object method during a test:
cy.stub(object, 'method');
Here, the object contains the method you want to stub. ‘method‘ is the method that will be replaced with the stub.
You can also define the behavior of the stub. For example, you can make the stub return specific values, throw an error, or track calls made to it.
const myStub = cy.stub().returns('hello'); myStub();
The stub returns hello in this example.
Why is the Cypress Stub() Method used?
The cy.stub() method is used to simplify the testing of functions that interact with external systems. By replacing these functions with controlled versions (stubs), you can:
- Eliminate External Dependencies: Isolate your tests from external services (like APIs) that may be unreliable or slow.
- Control Function behavior: You can manipulate the stub to return different values or simulate different scenarios (for example, success, or failure).
- Improve Test Reliability: Tests become predictable and deterministic, ensuring consistent results.
- Track Function Calls: Stubs allow you to verify how often a function was called and with which parameters.
Read More: How to run Cypress tests in parallel
Ways to do Stubbing in Cypress
There are several methods in Cypress for stubbing, each serving a specific purpose. Here, we will walk through the basic usage of cy.stub(), cy.intercept(), and cy.spy() to understand how stubbing works in different scenarios.
1. Basic Usage of cy.stub()
Consider a simple example where we stub a method that fetches user data:
describe('Stubbing Example', () => { it('stubs a method', () => { const userStub = cy.stub().returns({ name: 'John', age: 30 }); const getUser = () => userStub(); expect(getUser()).to.deep.equal({ name: 'John', age: 30 }); expect(userStub).to.have.been.calledOnce; }); });
We created a stub userStub that returns a predefined user object. The test checks that the stub was called once and returns the expected object. The test will pass if the stub behaves as expected.
2. Stubbing with cy.intercept()
cy.intercept() is used for intercepting network requests. It’s ideal for simulating API responses in a test environment without actually making real requests:
describe('Intercept Example', () => { it('intercepts an API call', () => { cy.intercept('GET', '/api/user', { name: 'Jane', age: 25 }).as('getUser'); cy.visit('/profile'); cy.wait('@getUser').its('response.body').should('deep.equal', { name: 'Jane', age: 25 }); }); });
We intercept a GET request to /api/user and return a predefined response. The test waits for the intercepted call and asserts that the response matches the expected result. The API call is intercepted, and the test confirms the response is as expected.
3. Stubbing with cy.spy()
A cy.spy() is similar to a cy.stub(), but instead of replacing a function’s behavior, it allows you to observe it. You can track the number of calls, arguments, and the return value.
describe('Spy Example', () => { it('spies on a method', () => { const myFunc = cy.spy().as('mySpy'); myFunc(1, 2, 3); expect(myFunc).to.have.been.calledOnceWith(1, 2, 3); }); });
The spy is used to track the method calls and arguments. The test checks that the function was called once with the expected arguments. The spy tracks the method call and ensures it was invoked with the correct parameters.
Read More: How to run specific tests in Cypress
Stubbing Functions and Properties Using cy.stub() Command
The cy.stub() command can be used not only for methods but also for properties. For instance, you can stub an object property to return a specific value:
const obj = { greet: () => 'Hello' }; cy.stub(obj, 'greet').returns('Hi');
In this case, stub the greet method always to return ‘Hi‘ instead of ‘Hello‘. This is useful when testing components that depend on certain property values.
Common Scenarios for Cypress Stubbing
Here are some common use cases where Cypress stubbing shines:
- Simulating API Responses: You can stub API calls to return mock data without actually hitting the backend.
- Testing Error Handling: Stub functions to simulate network failures, timeouts, or error responses, ensuring your app handles them gracefully.
- Controlling Timing: Control the behavior of time-dependent functions, such as timers, to make tests faster and more predictable.
- Isolating Components: Use stubbing to test individual components or functions without relying on external dependencies.
Difference Between Spies and Stubs
While both spies and stubs are used to monitor or replace functions, the key difference lies in their behavior:
- Spies: Track function calls but don’t alter the function’s behavior. They are used to verify that a function was called with specific arguments or the number of times it was called.
- Stubs: Replace a function’s behavior entirely, allowing you to define the return value or simulate certain behavior. Stubs provide the desired output regardless of how the replaced code is being used.
Difference between Stubbing and Mocking in Cypress
In Cypress testing, “stubbing” simulates how a dependency would behave. It uses predetermined responses to certain inputs so you can have control over the output without including the dependency.
With “mocking,” you gain better control over the dependency replacement, so you are allowed to set some expected responses and verify interactions like how many times it was called, with what parameters, and in which order of calls.
In a nutshell, stubbing is a lightweight, output-oriented form of testing, whereas with mocking, you have far more control over the output as well as the interaction with the dependency.
Read More: How to record Cypress Tests
Best Practices for Stubbing in Cypress
The following tips will help you use stubbing effectively for cypress testing:
- Use Stubs for External Dependencies: Always stub network calls or third-party services to avoid flaky tests.
- Keep Tests Independent: Avoid reliance on external APIs or databases by using stubbing for predictable behavior.
- Track Function Calls: Use cy.spy() or assertions like cy.stub().calledOnce to ensure your functions are being invoked correctly.
- Avoid Overuse: Stubbing should be used when necessary, but avoid stubbing too many functions as it can make your tests harder to maintain.
Why Run Cypress Tests on Real Devices?
Testing your web app on real devices is one of the most critical activities to ensure your application behaves right on multiple devices and environments. Here is why you must test on real devices:
- Test in real user conditions.
- Detect hardware and device-specific issues.
- Ensure cross-browser compatibility.
- Measure real-world performance metrics.
- Verify flawless responsive design.
- Deliver a smooth user experience.
While Cypress provides excellent browser testing, it cannot match the efficiency of real-world testing on actual devices. BrowserStack Automate allows you to run Cypress tests on real devices in the cloud so that your application can be thoroughly tested on various platforms without the complexity of running a device lab or setting it up.
You can run and scale your test suite in just one minute and configure the test environment how you want.
Conclusion
Mastering stubbing in Cypress allows developers to write more reliable, efficient, and isolated tests. Stubbing is vital in achieving consistent and effective tests, whether you’re simulating network requests, testing error handling, or isolating specific components.
By following best practices and understanding the difference between stubs, spies, and mocks, you can ensure your Cypress tests are precise, maintainable, and scalable.
With tools like BrowserStack Automate, you can extend the reach of your Cypress tests to real devices, further enhancing the quality and accuracy of your test suite.