🎭 Stubs, Spies, Clock & Fixtures
Stubs, Spies, Clock & Fixtures: cy.stub() completely replaces a function's original behavior and returns whatever you tell it to — like a flight simulator's "engine failure" butt
cy.stub() completely replaces a function's original behavior and returns whatever you tell it to — like a flight simulator's "engine failure" button: the real engine doesn't run (and shouldn't), but you need exactly that scenario to test the pilot's response. cy.spy() observes without intervening — the real engine stays on, but you record every RPM, every parameter. Here's the question worth asking: cy.intercept() already mocks network calls — so when do you actually need cy.stub()? Because cy.intercept() only intercepts the HTTP layer; to fake an in-app analytics function, a date API, or a third-party SDK method, cy.stub() is essential. In Java, this is the direct equivalent of Mockito's mock() vs spy() split: mock() controls all behavior, spy() wraps the real object and only intercepts selected calls. The real QA business risk: a test that calls the real payment service executes a live transaction on every run — if you don't wrap the payment gateway with cy.stub(), your CI pipeline can lose real money.
cy.stub() CHANGES a function's behavior — it returns a fake response or throws an error without running the real code. cy.spy() PRESERVES the function's original behavior, only tracking that it was called and with which arguments. This distinction is exactly the same as Mockito's mock() vs spy().
cy.stub() — fake response / error
Micro Lab: Cypress — API Intercept
Replace the TODO line with the critical line from the expected solution. This is not a real runtime; the goal is to reinforce writing the correct structure in a controlled way.
Why Does cy.stub() Completely Stop the Real Service?
cy.stub(paymentApi, "charge") wraps…
`cy.stub(paymentApi, "charge")` intercepts the payment API's charge function — from now on, if that function is called, the real code DOES NOT run; instead, you specify what response is returned.
`.returns({ success: true })` configures…
`.returns({ success: true })` defines the FAKE response that will be returned if the call happens — no real payment transaction ever occurs; the test constrains itself to the test environment.
Alternative: `.rejects(new Error(…))` simulates…
`.rejects(new Error("Gateway timeout"))` simulates a failure scenario (network error, timeout) — allowing you to test error handling without ever reaching the real service.
cy.spy() — keep original behavior, track the call
Why Does cy.spy() Run the Real Function AND Record Its Call?