Insights
Why Your Sandbox Tests Keep Failing: The Missing-Data Problem
Why Your Sandbox Tests Keep Failing: The Missing-Data Problem
The Flow passed every test in the sandbox. The deployment went through. Then, on Monday morning, production users started seeing errors, and the bug turned out to be impossible to reproduce in the sandbox.
Short answer: Most Salesforce tests that pass in a sandbox and fail in production fail because of the data, not the code. Sandboxes are often empty, missing related records, missing reference data like Price Books, owned by the wrong users, too clean, or far too small. Your code never meets the conditions production throws at it. The fix is to test against a realistic, related slice of production data.
You can rewrite the code three times and still ship the same bug if the sandbox cannot show you the problem. Below are the six forms the missing-data problem takes, the error each one produces, and how to fix it.
1. The sandbox is empty
Developer and Developer Pro sandboxes copy your metadata but no records. Most building happens in these sandboxes, so developers create a few records by hand and test against those.
- What you see: tests pass because there is almost nothing to process. List views, reports and dashboards look fine with three rows.
- What breaks in production: anything that depends on what other records exist, such as duplicate rules, roll-up summaries, "find the latest related record" logic, or matching against existing Accounts.
- Fix: load a realistic slice of production data into the sandbox before testing, rather than inventing records.
2. Relationships are broken
A Partial Copy sandbox copies a sample of records per object, chosen by a sandbox template. The sample does not guarantee that a record arrives with its parents and children. An Opportunity can arrive without its Account, or an Order without its Order Products.
- What you see: lookup fields are empty where production would never leave them empty.
- Errors this causes: Apex throws System.NullPointerException: Attempt to de-reference a null object when it reads a field on a missing parent. Flows fail on null record references, and formulas show blanks.
- Fix: copy records together with the records they depend on, parents before children, so every lookup resolves.
3. Reference data is missing
Some records exist only to make other records possible: the Standard Price Book and its entries, Products, queues and their members, territories, currency records. Test data built by hand usually skips them.
- What you see: you cannot add Products to an Opportunity, or assignment logic routes nothing.
- Errors this causes: STANDARD_PRICE_NOT_DEFINED when adding an Opportunity Product whose Product has no standard price, and FIELD_INTEGRITY_EXCEPTION when a Price Book Entry does not match the Opportunity's Price Book.
- Fix: load reference data first. Send Standard Price Book Entries before Opportunities, and treat them as part of every test dataset.
4. Records are owned by the wrong people
Users are separate records in every org. When test data is loaded without mapping owners, every record ends up owned by the admin who loaded it, or the load fails outright.
- What you see: sharing, approvals and assignment rules all behave as if one person owns the whole org.
- Errors this causes: INACTIVE_OWNER_OR_USER when a record points at a user who is inactive in the target, and approval or sharing logic that silently routes everything to the admin.
- Fix: map each source user to the matching target user before loading, and test while logged in as realistic users rather than as a System Administrator.
5. The data is too clean
Hand-made records are tidy: every field filled, sensible values, no history. Production data is not. It has null fields, very long text, picklist values that were retired years ago, records created before a validation rule existed, and odd currencies.
- What you see: everything passes, because the test data was written by the same person who wrote the logic.
- Errors this causes: FIELD_CUSTOM_VALIDATION_EXCEPTION when an update touches an old record that breaks a newer validation rule, plus formulas and Flows that never planned for a blank field.
- Fix: test against real records, including old ones. Filter your test slice to deliberately include legacy records and incomplete data.
6. There is too little volume
Governor limits and performance problems only appear at scale. Ten records never hit them; ten thousand do.
- What you see: fast pages and clean test runs in the sandbox, timeouts and limit errors in production.
- Errors this causes: System.LimitException: Too many SOQL queries: 101 from queries inside loops, Too many query rows: 50001, non-selective query errors on large objects, and UNABLE_TO_LOCK_ROW when thousands of children share one parent. Salesforce calls that last one data skew and recommends keeping fewer than 10,000 child records per parent.
- Fix: include realistic volumes and realistic parent-child ratios. Copy the large Accounts, not just the small ones.
How to tell if your failure is a data problem
Before you rewrite the code, ask five questions about the record that failed in production:
- Does a record like it exist in the sandbox? Same record type, same age, same owner type.
- Are all its lookups filled in the sandbox version? Check parents, Price Books and user fields.
- Is its owner active in the sandbox?
- Does it have far more related records than your test data? Count the children on its parent.
- Does it contain values your test data never used? Blanks, retired picklist values, very long text.
If any answer surprises you, the bug is most likely in the gap between your test data and production. The quickest proof is to copy that exact record, with its related records, into the sandbox and run the test again.
How to fix the missing-data problem for good
The lasting fix is to stop inventing test data and copy a targeted, related slice of production instead. That is called sandbox seeding, and it closes all six gaps at once:
- Empty sandbox: load real records, filtered to what you are testing.
- Broken relationships: bring parents and children along automatically, parents first.
- Missing reference data: send Price Books, Products and similar records before the records that use them.
- Wrong owners: map source users to target users before the load.
- Data too clean: include older and incomplete records in your filter.
- Too little volume: copy realistic amounts, including your largest Accounts.
Three habits keep it working. Pause triggers, Flows and validation rules in the target while loading, so they do not fire on every record. Save your seeding setup so you can re-run it after each sandbox refresh. And remove the seeded records when they go stale, so old data does not cause new false results.
SmartSeed is built for exactly this: you choose the objects and records, it brings the related records along in the right order, maps owners, and can later delete only what it created. For a step-by-step walkthrough, read our complete guide to Salesforce sandbox seeding. For other options, see 5 ways to create test data in Salesforce.
Frequently asked questions
Why does my code work in the sandbox but fail in production? Usually because production data is different: more records, missing or extra related records, inactive owners, and values your test data never used. The code is the same; the conditions are not.
Why do Developer sandboxes have no data? Developer and Developer Pro sandboxes copy metadata only. Only Partial Copy and Full sandboxes copy data on refresh, and Partial Copy copies a sample.
What causes STANDARD_PRICE_NOT_DEFINED in a sandbox? A Product has no entry in the Standard Price Book. Load Standard Price Book Entries before you create Opportunity Products.
How do I test governor limits in a sandbox? Run your tests against realistic record volumes and parent-child ratios. Small hand-made datasets rarely trigger SOQL, row or locking limits.
Is a Full sandbox the answer? It has all the data, but it refreshes only every 29 days, costs more, and copies all personal data. Seeding a targeted slice into Developer or Partial Copy sandboxes is often faster and safer.
Stop debugging the data
If your sandbox cannot show you the bug, it cannot help you fix it. Realistic, related test data turns "cannot reproduce" into a ten-minute fix.
SmartSeed is available now. Install it in a sandbox, or take our Sandbox Data Readiness Scorecard to see how closely your test environments reflect production.