Deliver and maintain › Testing and Debugging
Browser Developer Tools
On 22 May the Kestrel cart page was tested with three products in the cart. The products variable was present and it had a value, so at a glance the solution looked fine.
Reading the raw string showed something else. All three product IDs were inside one entry, joined by commas, instead of three entries separated by commas. Adobe would have read that as a single product with a very odd name.
That fault is only visible in the request itself. It was recorded against KES-S008 and the string was rebuilt as an array before the next cycle.
Rows KES-S005 and KES-S008 in the Tracker sheet are both products string faults, and both were found by reading a raw request. If you have not downloaded the file, you can get it now or come back to it later.
Why you still need the raw request
The debugger is quicker to read and it is not always available. Three situations come up regularly.
The fault happens on somebody else's machine, in another office, on a locked down browser with no extensions. The fault only happens in a browser the extension does not support. Or the debugger shows something that makes no sense and you need to confirm it somewhere neutral.
There is also the case above. Where the exact characters matter, a formatted view can hide the problem, and the raw string is the only place the structure is visible.
Finding the hit
Open developer tools, go to the Network tab, and filter. What you filter for depends on which library the site uses.
AppMeasurement filter: /b/ss/ https://metrics.kestrelandco.com/b/ss/kestrelprod/1/... Everything is in the URL. One request per hit. Web SDK (Edge Network) filter: /ee https://edge.adobedc.net/ee/v1/collect?configId=... Everything is in a JSON payload. Read the Payload tab. Both can also be found by filtering on the collection domain, which helps when a site uses its own first party domain and the path has been customized.
Two settings make this usable, and both have to be on before you do the action. Preserve log keeps requests across page navigations, without which a checkout clears the evidence at every step. Disable cache stops the library being served from an old copy, which matters on the day you are testing whether a fix deployed.
Reading a hit without help
An AppMeasurement request is a query string. The parameter names are short because they were designed when URL length mattered. There are dozens. These are the ones that answer real questions.
| Parameter | Holds | Worth checking because |
|---|---|---|
The path after /b/ss/ | The report suite ID | Correct data in the wrong suite looks exactly like no data |
pageName | The Page dimension | An empty one falls back to the URL, which fills a report with query strings |
g and -g | The page URL, first 255 bytes and then the rest | A long URL is split across two parameters rather than truncated |
r | The referring URL | Empty referrers turn paid traffic into direct traffic |
c1 to c75 | Props | c4 is prop4. The number is the only mapping there is |
v1 to v250 | eVars | v19 is eVar19. An eVar missing here can still report a persisted value |
events | The comma separated event list | An event missing here cannot appear in any report, whatever the rule claims |
products | The products string | Position decides meaning. Position three is quantity, and it is the usual fault |
pe and pev2 | The link type, and the link name | Their presence means this is a link hit rather than a page view |
mid and aid | Experience Cloud ID, and the legacy Analytics ID | A missing mid points at the identity service, not at Analytics |
cc | The currency code of the hit | A wrong one converts every revenue figure on it |
ts | A custom timestamp, when one is set | It changes when the hit lands, and so changes what you should expect to see |
Read them in a fixed order rather than looking for the one you care about. Report suite, then hit type, then the variables the design names. The Kestrel cycle found two faults that no single variable would have shown: a whole journey landing in the wrong suite, and a page view firing where a link was intended.
The products string deserves its own look, because position decides meaning and two of the Kestrel faults were there. KES-S005 sent the cart total in position three instead of the quantity being added, so adding one product reported two. KES-S008 was the malformed string in the opener. Both look like a populated value until you read the characters.
AppMeasurement libraries from 1.4.1 onward send an image request over HTTP POST when it grows too long, and data sent that way is not truncated at any length. So a large hit, usually one carrying a full products string, can show almost nothing in the URL while every variable sits in the request body. Reading only the query string and concluding the variables were dropped is one of the most common false findings in this work. Open the Payload or Request tab and read the body. Web SDK requests always need that, because they carry a JSON payload rather than a query string.
The console, and two things worth doing in it
The Network tab shows what left the browser. The console shows what the page knew, which is the other half of every useful comparison.
// Whatever object the site publishes. Read it at the moment of the fault.
window.digitalData
window.adobeDataLayer
window.dataLayer
// Adobe Client Data Layer keeps a history, which is the useful part.
// It shows what was pushed and in what order, not just the end state.
window.adobeDataLayer.getState()
// Make the tag library report which rules fire, for this session
// and this site only. Set it before you do the action.
_satellite.setDebug(true)
// The Web SDK equivalent. The URL parameter needs no console at all,
// which matters on a browser you cannot type into.
alloy("setDebug", { "enabled": true })
?alloy_debug=trueRead the data layer object first, then do the action, then read the request. Those three readings in that order answer most questions in about a minute.
Typing s.t() or s.tl() into the console of a live site sends a real hit into the real report suite, and it cannot be recalled. The data is in the report, it will be in the month end figures, and there is no undo. Adobe will not remove it, because nothing went wrong. Test firing belongs in a development report suite. Doing the real action on the page is almost always better than simulating one. A simulated call proves the library works. It proves nothing about the rule that should have called it.
Telling a data layer fault from a rule fault
This is the test the section exists for. It settles the question that otherwise runs for a week between an analytics team and a web development team.
A missing value in a report has three possible causes, and from the report all three look the same. The site never provided the value. The site provided it and the rule failed to carry it. Or both worked and something inside Adobe changed it. Two comparisons separate all three, and they take about a minute.
Solution KES-S004 went through this on 22 May. Every product detail page view arrived empty. The request was missing the product values, so the path goes down. In the console the product objects were present and correct by the time anybody looked.
That combination looks contradictory and it is the useful clue. The data layer was right when a person checked it, and empty when the rule read it, because the page load rule ran before the site had populated the objects. The repair was to change it to a direct call the site fires once its own values are set. That is recorded in the technical document change log, which is what stops somebody changing it back.
What the two comparisons really buy is not speed. The test names the owner from evidence both teams looked at together, so the conversation moves straight to the repair instead of to whose fault it is.
Conversion variables persist, so a value set on the product page keeps being credited to later hits in the visit without appearing in any of them. So somebody reads a purchase hit, finds no v19, and reports Payment Method as broken. They have found nothing, if the design sets it at the payment step and expires it at the end of the visit. Read the request against the design for that specific moment, not against a general expectation. Allocation and expiration are covered in eVars (Conversion Variables).
Single page applications lose hits in two ways
A site that changes screens without reloading the page causes two different problems, and it is worth knowing both before testing one.
The first is about evidence. The browser does not clear the request list, so hits pile up from every screen in the journey. That is helpful once Preserve log is on and confusing before, because the hit on screen may belong to a screen you left three steps ago. Read the timestamp column.
The second is the real fault. A page load rule fires once, at the actual page load, and never again. Every later screen is a change in the application's own state that the browser knows nothing about. Unless the site announces each one, four screens produce one page view.
Kestrel had the opposite version of this on KES-S002. The direct call fired on the first load as well as on each step change, so the checkout counted one page view too many. Both faults are found the same way: do the action, count the requests, and compare that count against the design.
Capturing evidence for the row
A finding that will be disagreed with needs evidence attached. The validation report expects it on every solution that passes, and it takes a few seconds.
Use Copy as cURL or save the request, and take a screenshot named for the solution ID. That settles arguments permanently, and it is the only defense against the most awkward situation in implementation work: a solution that passed testing and is wrong in production. With evidence you can say exactly what was seen on staging, and the conversation becomes about what changed between the two environments. Without it, the conversation is about whether the testing was any good.
Capturing a hit properly
Run this when the finding is going into a test cycle, which in practice means most of them.
- Part one, before you touch the page
- Open developer tools, go to Network, and switch on Preserve log and Disable cache. Both before you act. Neither can be applied to a request that has already gone.
-
Filter for
/b/ss/or/ee, then clear the list so it starts empty. An empty list at the moment you act makes it obvious whether the action produced a hit or produced nothing. -
Run
_satellite.setDebug(true)and read the data layer object once, before acting. The before state is half the comparison and it cannot be recovered later. - Part two, the capture
- Do the one action, then stop. Do not click anything else. One action, one set of hits. A second click makes it impossible to say which request came from what.
- Count the requests before reading any of them. Two where the design says one is a finding on its own, and it disappears if you open the first hit and start reading variables.
- Read the report suite, then the hit type, then the variables the design names for this moment. In that order, every time. The first two checks catch faults that no amount of variable reading will reveal.
- If the query string looks short or empty, open the Payload or Request tab and read the body. Long hits travel by POST and Web SDK hits always do. This step alone prevents a whole category of false finding.
- Part three, record it
- Save the request or copy it as cURL, and name the screenshot after the solution ID. A finding with the raw hit attached ends a disagreement. A finding described from memory starts one.
- Write the finding into the row as what you observed, with today's date, and set the status. All three product IDs are in one entry rather than three beats products string malformed, and it stays true even if your diagnosis is wrong.
That is a hit captured, read and recorded. Where a fault only affects some visitors, repeat the same capture on a throttled connection and in a second browser. The difference between the two captures is usually the answer.
What you have now
The ability to read an implementation on any machine, including one you do not control. And a two comparison test that names which team owns a repair, before anybody argues about it.
Every check in this section stops at the moment the request leaves the browser. A hit that is perfect on the way out can still be discarded by a bot rule. Or changed by a processing rule, deduplicated against another visitor's purchase ID, or credited to a channel nobody expected. All of that happens where you cannot watch, and none of it reports an error.
So a correct request is necessary and not sufficient, and the last check is always the round trip. Real-Time Validation covers what happens in that gap, how quickly you can see the far side of it, and the one variable type real time reporting will not show you.
This article focuses on the concepts, architecture, and practical guidance behind the topic. For the latest UI walkthroughs and step-by-step implementation instructions, use the links below. They leave this site and open Adobe's own documentation in a new tab.