dataLayer testing: how to validate ecommerce events before they corrupt your reports
The short answer: write down what each ecommerce event must contain, capture what the page actually pushes to window.dataLayer, and compare the two automatically. A JSON Schema is the simplest way to write the contract, and a validator tells you exactly which field broke.
Eyeballing the dataLayer in Tag Manager preview catches missing events. It rarely catches a value that quietly became a string.
The dataLayer is the contract between your site and your tags. When a developer, a theme update or an app changes its shape, every tag that reads it keeps firing, on the wrong data. Nothing errors. The numbers just drift.
What breaks in a dataLayer (the failure patterns we built around)
These are the defects that pass a casual check and still corrupt reporting:
| Defect | Looks like | Effect |
|---|---|---|
| Value is a string | "value": "49.99" | Platforms may ignore or misread revenue |
| Missing currency | no currency key | GA4 can drop the revenue; purchases show £0 |
| Lowercase or malformed currency | "gbp", "£" | Not a valid ISO 4217 code |
| Empty items | "items": [] | Event counted, no product data |
| Item price as a string | "price": "12.00" | Item revenue wrong or missing |
| No transaction_id on purchase | missing or empty | Duplicates cannot be removed |
| Stale ecommerce object | items from the last event leak into the next | Wrong products credited |
| Event renamed | Purchase vs purchase | Tag Manager trigger never matches |
Step 1: look at what the page pushes
On any page, open the browser console and type dataLayer. You will see every push so far. For a readable view, open Google Tag Manager › Preview: Tag Assistant lists each push as a step, with every variable's value at that moment. Walk the purchase path and check each ecommerce event appears, in order, with a populated ecommerce object.
A well-formed GA4 purchase push looks like this:
dataLayer.push({ ecommerce: null }); // clear the previous ecommerce object
dataLayer.push({
event: "purchase",
ecommerce: {
transaction_id: "1234",
value: 59.98, // a number, not "59.98"
currency: "GBP", // ISO 4217, uppercase
items: [
{ item_id: "SKU-1", item_name: "Canvas tote", price: 29.99, quantity: 2 }
]
}
});
Step 2: write the contract down as a schema
Checking by eye does not scale past one page on one day. Write the rules down once, as a JSON Schema, and let a validator enforce them. The rules that matter most for GA4 ecommerce:
eventis one of the ecommerce events you implement (view_item,add_to_cart,begin_checkout,purchase,refund…).ecommerce.currencymatches^[A-Z]{3}$.ecommerce.valueis a number, zero or above.ecommerce.itemsis an array with at least one item, each with a non-emptyitem_idanditem_name; where present,priceis a number andquantityan integer of at least 1.- For
purchase:transaction_id,value,currencyanditemsare all required. - For
refund:transaction_idandcurrencyare required.
Those are exactly the rules AuditTag's own ecommerce schema enforces. We deliberately validate the fields that cost money when they break, not every optional GA4 parameter, so a failure always means something worth fixing.
Want a baseline first? The free AuditTag audit checks which tracking tags are detectable on your public pages before you dig into the dataLayer. Run a free audit →
Step 3: capture the dataLayer automatically
To test without a person clicking, a headless browser does the walking. With Playwright, for example, a script loads a product page, clicks add to cart, moves through checkout on a test payment method, and reads window.dataLayer at each step. The captured array is then passed to the validator. Each violation comes back with its exact path, such as $.ecommerce.items[0].price: expected number, got string, so a developer can go straight to the line.
Step 4: run it at the moments things change
- In CI, against staging, before release. A failing purchase schema should fail the build, the same way a failing unit test does.
- After every deploy, against production. Staging and production drift; confirm the release that shipped is the one you tested.
- On a schedule. Apps, tag manager publishes and platform updates change the dataLayer without going through your deploys at all.
Severity should follow money: a malformed purchase or refund is critical, because ad platforms bid on it; a drifted view_item is a warning worth fixing before it reaches checkout.
How AuditTag handles it
What AuditTag checks
- Every captured ecommerce push validated against a JSON Schema for GA4 ecommerce events
- Purchase and refund violations reported as critical; upper-funnel drift as a warning
- Each violation reported with its exact path and reason
- Deterministic: the same dataLayer always produces the same findings, so it can run on every deploy
What this doesn't prove
A valid dataLayer doesn't prove your tags read it correctly or that the platforms received the event. The free audit reads served HTML and does not capture a live dataLayer; schema-drift checking runs in Guardrails, which is in early access, with capture set up hands-on per site.
Catch dataLayer drift before it reaches your reports
Start with the free audit of your public pages, then talk to us about Guardrails if you want your ecommerce events validated after every deploy.
Run a free audit →Questions people ask
How do I see what is in the dataLayer?
Open your browser's developer console on the page and type dataLayer, then press Enter. You will see every object pushed so far, in order. Google Tag Manager's Preview mode (Tag Assistant) shows the same pushes as a timeline, with the values of each variable at every step, which is easier to read.
What is dataLayer schema validation?
It is checking every ecommerce push against a written contract, usually a JSON Schema, that says which fields must exist and what type each must be. Instead of a person eyeballing values, a validator reports exactly which field broke, for example ecommerce.value expected number but got string.
Why should I push ecommerce: null before an ecommerce event?
Tag Manager merges successive dataLayer pushes into one data model. If you do not clear the previous ecommerce object, fields from an earlier event, such as the items from a product view, can leak into the next one. Google's own GA4 ecommerce examples clear it first with dataLayer.push({ ecommerce: null }).
Can dataLayer testing be automated?
Yes. A headless browser such as Playwright can load pages, perform actions like add to cart, capture window.dataLayer, and validate each push against a schema. Run it in CI against staging to block a broken release, and on a schedule against production to catch changes made outside your deploys.