The most dangerous test result in a Salesforce program isn’t a red one. It’s a green one that’s wrong.
Picture a quote flow. The test fills in the loan amount, term, and credit tier, clicks Generate quote, sees the success toast, and passes. Every step worked. But the rate on the quote came from the previous tier because a flow read a stale field, and nobody will notice until a customer, an auditor, or a pricing analyst does.
I’ve spent years testing Salesforce in financial services: leads and opportunities, quotes that double as applications, nCino loan origination, private wealth pipelines, and pricing engines where the price is derived from a dozen inputs. The pattern is the same everywhere. UI automation is necessary, and on its own it’s not enough.
Why the screen lies
In a modern Salesforce org, most of the business logic doesn’t live on the page. It lives in flows, triggers, Apex, validation rules, assignment rules, and integrations that fire after the user clicks save. The page shows a toast because the save succeeded. Whether the right thing was saved is a different question, and the UI isn’t built to answer it.
The failures I see most often:
- Wrong price, right screen. A pricing input is read from the wrong record, a tier boundary is off by one, or a rounding rule differs between the calculator and the stored value.
- Misrouted records. A loan application lands in the wrong queue, or with the wrong owner, because an assignment rule matched on an outdated field.
- Half-finished automation. A flow updates the parent record but silently fails on a child, so the stage moves forward while the documents never generate.
- Broken hand-offs. The record is perfect in Salesforce, and the downstream system receives a payload with a missing field.
Test the record, not just the screen
The fix is to finish every UI journey with checks at the data layer. After the test drives the screen the way a user would, it queries the record through the API and asserts on what was actually stored.
// The UI drives the journey. The data decides the verdict. test('QUOTE-112 prices a 30-year loan at the right tier', async ({ page, sf }) => { const quoteId = await quotePage.create(inputs); // user path const [quote] = await sf.query( `SELECT Rate__c, Pricing_Tier__c, OwnerId FROM Quote__c WHERE Id = '${quoteId}'`); expect(quote.Pricing_Tier__c).toBe(expectedTier(inputs)); // independent oracle expect(quote.Rate__c).toBeCloseTo(expectedRate(inputs), 4); expect(quote.OwnerId).toBe(queues.residentialLending); });
Two details matter here. First, the expected rate comes from an independent oracle: a pricing table the business owns, not a copy of the logic under test. If you recompute the price the same way the system does, you’ll faithfully reproduce its bugs. Second, the test checks routing and ownership, not just values, because a correct loan in the wrong queue is still a missed SLA.
Design the data like a pricing analyst would
Pricing and eligibility bugs cluster at boundaries. So the test data shouldn’t be “a typical customer.” It should be a matrix: each credit tier at its lower and upper edge, each term, amounts just under and just over each threshold, and the combinations the business says are rare but expensive. Kept in a table the business can read, that matrix becomes both your data-driven test suite and a conversation with the pricing team about what “correct” means.
When a test fails, read what the org did
A data-layer failure tells you what is wrong. Salesforce debug logs usually tell you why: which flow fired, in what order, and which field it read. Capturing the relevant log alongside a failed test turns a two-day investigation into a twenty-minute one, and it gives the developer evidence instead of a screenshot.
Follow the journey past Salesforce
For a bank, the journey rarely ends in Salesforce. An application submitted online has to land correctly in the CRM, then in loan origination, then in the core systems behind it. We test that horizontally: one business journey, verified at each system it touches, including the API calls in between. A green result has to mean the customer’s loan is right everywhere it lives.
Takeaways
- End every critical UI journey with assertions on the stored record.
- Compute expected values from an independent source the business owns.
- Check routing and ownership, not just field values.
- Build test data around pricing and eligibility boundaries.
- Capture debug logs with failures, and follow journeys into downstream systems.