Most test data generators create records and stop there. They fill required fields, respect validation rules, and call it done. But if your org relies on role hierarchies, org-wide defaults, or criteria-based sharing rules, a flat pile of records tells you nothing about who can actually see what. Testing sharing rules means generating data with ownership, role assignment, and territory context baked in from the start, not bolted on afterward.

This matters more than most QA plans admit. A sales rep who can suddenly see opposing territory opportunities, or a partner user who can view internal-only cases, is not a cosmetic bug. It is a compliance problem and, in regulated industries, a reportable one. Sharing rule failures rarely show up in unit tests because Apex test context often runs as the system or as an admin user who bypasses sharing entirely unless you explicitly declare "with sharing" and query as a specific user.

Why Generic Test Data Hides Sharing Rule Bugs

Record-level security in Salesforce depends on four things working together: organization-wide defaults, role hierarchy, sharing rules, and manual shares. A test data set that only varies field values but assigns every record to one admin user never exercises any of this. Every query returns everything, every report looks correct, and the gap only surfaces in production when a real user with a real role tries to run the same report.

The fix is not more records. It is more realistic ownership. You need test users distributed across your actual role hierarchy, with account and opportunity records owned by those specific users, so that private and public-read-only OWD settings actually get tested under load.

SproutEzee reads your org's role hierarchy and user list during schema discovery, which means generated records can be assigned to specific roles rather than defaulting to the running user. That single change turns a sharing rule test from theoretical to actual.

Map Roles and Territories Before You Generate a Single Record

Start by pulling your role hierarchy and any territory model you use. If Enterprise Territory Management is active, territories add another sharing layer on top of roles, and test data needs account ownership that aligns with territory assignment rules, not just a role lookup.

Build a simple mapping table before generation: role name, OWD-affected objects that role should see, and the number of synthetic users needed per role. A three-tier hierarchy (Sales Rep, Sales Manager, VP Sales) with ten users per tier gives you enough spread to catch rollup and hierarchy-based sharing issues without generating noise.

Role LevelTypical OWD ExpectationTest Focus
Sales RepPrivate on Account/OpportunityCan only see own-owned records
Sales ManagerPrivate with role hierarchySees own team's records, not peer teams
VP SalesPrivate with role hierarchySees all records below in hierarchy
Partner Community UserPrivate, criteria-based shareSees only records matching share criteria, never internal-only fields

This table becomes your generation spec. Each row tells SproutEzee which users own which batch of records and what the pass/fail check looks like once data lands.

Generating Owner-Aware Records With Bulk API v2

Bulk API v2 jobs accept an OwnerId field on most standard and custom objects, so distributing ownership across your mapped users is a configuration choice, not a custom script. The practical challenge is volume math: if you need 50,000 opportunities spread realistically across 30 sales reps reporting to 5 managers, a flat random distribution will not mimic a real pipeline. Real orgs show skew, with top performers owning disproportionately more open deals.

SproutEzee lets you weight ownership distribution per role during generation, so you can model that skew intentionally rather than discovering it as an unplanned side effect. Weighted distribution also stress-tests sharing rule performance, since Salesforce recalculates sharing recursively down the hierarchy whenever ownership changes at scale, and a lopsided ownership model surfaces recalculation lag that an even distribution never would.

Keep territory assignment rules in the same job. If territory membership is criteria-based, on BillingState for example, generate account records with BillingState values that map cleanly to your territory model rather than random geography. Random state values will scatter records across territories unpredictably and make it impossible to isolate a sharing rule bug from a territory assignment bug.

Testing Criteria-Based and Owner-Based Rules Side by Side

Owner-based sharing rules extend visibility from one role or public group to another based on who owns the record. Criteria-based sharing rules extend visibility based on field values, independent of ownership. Test data needs to exercise both, and they need separate verification, because a record can pass one check and silently fail the other.

For owner-based rules, generate opportunities owned by a public group member and confirm the shared group gains read or edit access as configured. For criteria-based rules, generate records that straddle the boundary condition. If a rule shares all cases where Priority equals "Critical", generate records at exactly that value and one step away, such as "High", to confirm the rule does not over-share.

Boundary-value generation is where most hand-built test data falls short, because someone manually creating ten test records rarely bothers to test the edge. Schema-aware generation can target picklist boundary values automatically once you flag which fields drive criteria-based rules, which turns an easy-to-skip test into a repeatable one.

Verifying Visibility After the Load

Generating the data is half the job. The other half is querying as each synthetic user to confirm visibility matches expectation. Apex supports this directly: run a SOQL query inside a System.runAs() block for each test user role, count returned records, and compare against your mapping table from earlier.

Automate this as a post-load validation step rather than a manual spot check. A script that loops through your role-to-user mapping, runs the same query as each user, and flags any count that deviates from the expected range catches regression the moment a sharing rule changes, not weeks later when a user complains. This is also where you catch the subtle Salesforce behavior where an inactive user's sharing rules stop evaluating silently, inflating or collapsing visibility without any error log entry.

Run this verification after every sandbox refresh, not just after initial data load. Sharing rule configuration drifts between sandboxes more often than most teams expect, especially when a partial sandbox pulls fresh metadata but stale sharing rule records.

Keeping the Test Data Maintainable

Role hierarchies change. New territories get added. A test data set hardcoded against today's org structure becomes brittle fast. Build the generation job to reference the role hierarchy dynamically at run time rather than hardcoding role IDs, so a reorg does not require a rewrite of your test data scripts.

Store your role-to-record mapping table as org metadata or a custom setting rather than a spreadsheet nobody updates. That keeps the mapping discoverable by anyone running the generation job later, including a new admin who inherits the process without full context on why ten sales reps need opportunities skewed 70/30 toward two specific users.

None of this requires exotic tooling. It requires treating ownership and role assignment as first-class generation parameters rather than an afterthought bolted onto field-level mock data. Get that right once, and every sandbox refresh after it inherits a sharing rule test that actually means something.

Frequently Asked Questions

Why does flat test data fail to catch Salesforce sharing rule bugs?

Flat test data usually assigns every record to one admin or system user, which bypasses role hierarchy and org-wide default checks entirely. Sharing rule bugs only surface when records are owned by users at different hierarchy levels and queried under their actual access context. Without varied ownership, every test query returns the full data set regardless of whether sharing rules are configured correctly.

Can Bulk API v2 set the OwnerId field during test data generation?

Yes, Bulk API v2 accepts OwnerId on most standard and custom objects as part of the insert payload. This allows a generation job to distribute records across a mapped set of test users spanning different roles and territories. Weighting that distribution to mimic real pipeline skew gives a more accurate sharing rule test than an even random spread.

How do I test criteria-based sharing rules with synthetic data?

Generate records at the exact boundary value the sharing rule evaluates on, plus one value just outside that boundary, to confirm the rule shares correctly without over-sharing. For example, if a rule shares records where a priority field equals Critical, create records at Critical and at the next value down, such as High. Comparing visibility results between the two confirms the rule's logic rather than just its existence.

Does Enterprise Territory Management add a separate layer to test?

Yes, territory assignment rules operate independently of role hierarchy and org-wide defaults, so they need their own test pass. Account fields that drive territory assignment, such as billing state or industry, should be generated with values that map deliberately to your territory model rather than random geography. Random values make it hard to isolate whether a visibility failure comes from a sharing rule or a territory assignment mismatch.

How often should sharing rule visibility be re-verified after data generation?

Re-run verification after every sandbox refresh, not only after the initial load, since sharing rule configuration and metadata can drift between refreshes. Automating the check with a script that queries as each test user role and compares record counts against expected ranges catches regressions immediately. Manual spot checks tend to miss silent failures, such as an inactive user whose sharing rules stop evaluating without generating an error.