Here is a question that came from (deprecated) Cypress Gitter chat channel. The user provided a repo with a reproducible test that tried to assert that window.dataLayer.push
method was called with an object with the field event: lead
. This is just one particular call among hundreds of calls the application is making to track its various events.
The user is struggling to write the correct test and asked me to help.
The original test
1 | describe('testin datalayer', () => { |
A few observations about this test right away:
- the base URL is hardcoded in the test, which is an anti-pattern. I prefer setting the base URL in the
cypress.json
file, watch the video How to correctly use the baseUrl to visit a site in Cypress - there are several hard-coded
cy.wait(ms)
commands, probably to let the page load the tracking library and create thewindow.dataLayer
object. We can specifically wait for thewindow.dataLayer
to exist, avoiding the hard-coded waits
Updated test
Let's start by moving the base URL to the cypress.json
file and removing the cy.wait(ms)
commands. Instead we will wait for the window
object to have a property dataLayer
with the method push
. See the inline code comments for the full explanation and links to more documentation.
1 | describe('testing datalayer', () => { |
Spying
Ok, what about our spy? Sure, let's add a spy on the method push
of the dataLayer
object. The most interesting thing here is how to check if the spy function was called by the application. We have complex calls, and are only interested in the call where the first argument is an object with property event: "lead"
. Luckily, Chai-Sinon assertions included with Cypress provide a way to use a custom matcher to check the calls.
1 | describe('testing datalayer', () => { |
For more Chai-Sinon assertion examples, see my cy.stub
, cy.spy
, and cy.clock
examples. For now, the test works, even if it is pretty noisy - there are lots of dataLayer.push
calls!
Cut the noise
We only want to be informed about dataLayer.push(lead)
events, not every call. Unfortunately, the built-in Sinon mechanism to create a targeted spy cy.spy(dataLayer, 'push').withArgs({ event: 'lead' }).as('lead')
expects the exact argument match, not part of the object. We could use the same match via predicate when creating a spy:
1 | cy.spy(dataLayer, 'push') |
It does simplify the assertion
1 | cy.get('@lead').should('have.been.calledOnce') |
Unfortunately, this still records other calls to dataLayer.push
method âšī¸
We really need to avoid using cy.spy
for calls we are not interested in. We can do this by constructing a "plain" Sinon.js stub function avoiding the cy.stub
or cy.spy
command to avoid logging every call. Here is the entire test, including printing the event data to the Command Log.
1 | describe('testing datalayer', () => { |
The test is beautiful đĨ° and takes only four seconds đ instead of 20+ seconds of the original test with hard-coded waits.
See also
- Spy On DOM Methods And Properties
- Cypress Stubs, Spies, and Clocks Guide
- my
cy.stub
,cy.spy
, andcy.clock
examples - find more stubbing and spying examples by searching using my cypress.tips/search page
- Sinon.js documentation