Receipts
Every figure here is produced by running something, and each block names what.
What does it print when I point it at a real org?
# what will run, before running it docker run --rm -v ./npsp/force-app:/mdapi:ro --entrypoint /sfapex fidelic/emulator coverage /mdapi COVERAGE parsed: 1061 files (0 with ERROR nodes, 0 error nodes total) found: 26 trigger(s), 1503 class(es) RUNG B LADDER @ C1 + C2 families {async, callout, crypto, database, recorded-effect, schema, schema-permission, settings} (load-time classification): EXECUTE=7 HOOKS=0 UNRESOLVED=19 RECORDED-ONLY=0 (SIMULATED=1) EXECUTE TDTM_DataImportBatch [Provisional: schema: describe labels derived where the metadata carries none; DisplayType modeled as its enum name; == identity on describe results Provisional (SCHEMA-DESIGN §7)] EXECUTE* TDTM_FormTemplate (SIMULATED: settings: Gift_Entry_Settings__c.getInstance — Custom Setting read at C2; settings: Gift_Entry_Settings__c.getOrgDefaults — Custom Setting read at C2) [Provisional: schema: describe labels derived where the metadata carries none; DisplayType modeled as its enum name; == identity on describe results Provisional (SCHEMA-DESIGN §7)] UNRESOLVED TDTM_HouseholdObject (min tier C3; blocked by dispatch: interface Evaluator [C3]) # now boot it, with the row-lock fault armed docker run -p 8080:8080 -v ./npsp/force-app:/mdapi:ro -e SFEMU_FAULT_LOCK_ON_UPDATE=true fidelic/emulator --apex-src /mdapi sfemu: apexexec ON — 26 triggers loaded from "/mdapi", 7 EXECUTE-classified sfemu … listening on :8080 (store: sqlite ":memory:", client-secret: false, metadata: "/mdapi", hooks-file: "") # 14.1s to listening — NPSP, 26 triggers / 1,503 classes, 3-vCPU cloud VM curl -sX PATCH localhost:8080/services/data/v62.0/sobjects/Account/001000000000038AAA \ -H 'Content-Type: application/json' -d '{"Rating":"Hot"}' HTTP 400 [{"message":"unable to obtain exclusive access to this record or 1 records", "errorCode":"UNABLE_TO_LOCK_ROW","fields":[]}]
Reproduce: the three commands are in the block, run against
NPSP @ 19646faa
(force-app) on fidelic/emulator:4ea9d49. Three of the 26 trigger
rows are shown.
Does the emulator match Salesforce behaviour?
75 of 75 conformance tests pass.
Denominator: 75 tests in the public fidelic-conformance suite that can run against a stock emulator; 5 skipped for an org prerequisite and 3 pending real-org verification are excluded from both sides of the fraction
Reproduce: go run ./cmd/confrun --target=emulator (fidelic-conformance)
Which of my org's triggers will actually run?
51 of 177 trigger-context pairs classify EXECUTE, across 8 of 26 triggers. That is 28%.
Denominator: ALL 177 trigger-context pairs declared by the 26 triggers in this corpus. This is NOT the runtime ladder's denominator, which counts only the pairs declared by classified-EXECUTE triggers and is therefore a smaller number over a different population.
Reproduce: sfapex coverage --json <corpus> against NPSP @ 19646faa (force-app)
Is that number just true of the org you picked?
142 of 554 trigger-context pairs classify EXECUTE across five public Salesforce codebases — 23 of their 82 triggers.
| Codebase | Triggers | EXECUTE | Refused: our gaps | Refused: needs org files | Pairs | % |
|---|---|---|---|---|---|---|
NPSP 19646faa | 26 | 8 | 2 | 16 | 51/177 | 28% |
EDA b311845 | 37 | 11 | 1 | 25 | 77/259 | 29% |
DLRS 8e43f01 | 14 | 0 | 0 | 14 | 0/97 | 0% |
apex-recipes 87c1c9b | 3 | 2 | 1 | 0 | 2/9 | 22% |
fflib-samplecode 4657d63 | 2 | 2 | 0 | 0 | 12/12 | 100% |
Denominator: every trigger-context pair declared by every trigger in the checked-out source, at the commit named beside each codebase. A refused trigger is counted against our gaps only when nothing else blocks it; anything that also reaches a missing file is counted there instead, so the two columns add up to the refusals and never double-count. DLRS reads 0% because all 14 of its triggers reach a managed package or an object whose metadata the repo does not ship.
A public checkout is not an org. It carries the Apex but not the managed packages, the object metadata, or the configuration records that live as data — which is why 55 of these 59 refusals are files rather than features. An org export carries them, so the number that matters is the one from your own: run the command below against it.
Reproduce, per codebase: docker run --rm -v "$PWD/force-app":/mdapi:ro --entrypoint /sfapex fidelic/emulator coverage /mdapi
How much of a real Apex test suite actually runs?
125 of 793 test classes run every method with nothing refused — 15%. 760 of 7,518 methods report a result: 617 pass, 143 fail.
| Codebase | Test classes | Test methods | Classes the analyzer admits | Classes that run clean | Methods with a result | Passed | Failed | Refused at runtime |
|---|---|---|---|---|---|---|---|---|
| apex-recipes | 61 | 322 | 32 | 2 | 15 | 8 | 7 | 69 |
| dlrs | 66 | 838 | 8 | 0 | 1 | 1 | 0 | 44 |
| eda | 288 | 2,357 | 214 | 114 | 706 | 583 | 123 | 577 |
| fflib-apex-common-samplecode | 10 | 16 | 1 | 0 | 0 | 0 | 0 | 1 |
| npsp | 368 | 3,985 | 62 | 9 | 38 | 25 | 13 | 267 |
Denominator: ALL 7518 @isTest methods in 793 test classes across five pinned open-source Salesforce codebases (apex-recipes, dlrs, EDA, fflib sample code, NPSP). Not the methods we admitted — the whole suite, including everything refused.
Two columns disagree on purpose. Classes the analyzer admits is what we predict can run; classes that run clean is what did. The difference is 60% of the prediction, and it is published because a page that showed only the prediction would be claiming 317 classes where 125 is the measured answer. We are closing that gap in the open; until it is closed, the smaller number is the one that counts.
A failure is not an accusation. It means the assertion compared two values and they differed — sometimes this emulator diverging, sometimes a test that depends on org state we do not model. We have not split them and do not claim the split.
The failed/refused split is drawn conservatively. Only the runner's own
Assertion Failed prefix counts as a failure; every other non-passing
message counts as our refusal, including ones that surface inside your test — a
QueryException on an object we carry no schema for, or a wrong-arity call
because method overloading is not modelled. The looser reading would move several
hundred results out of refused and into failed, improving every
percentage on this page. It is not used.
Reproduce: boot against your source, then POST /__emulator__/testrun — GET reports what would run, POST runs it. The commits for each
codebase are the ones named in the trigger table above.
What do EXECUTE, SIMULATED and UNRESOLVED mean?
- EXECUTE
- No blocker was found in the reachable code, so the interpreter will attempt your real Apex.
- SIMULATED
- Your code runs. One or more named things were modelled instead of performed, and each one is named.
- Provisional
- The behaviour is faithful. The response envelope has not yet been confirmed against a real org. A capability can carry this label and SIMULATED at the same time.
- UNRESOLVED
- Declined by name, with the file and line that caused it.
- RECORDED-ONLY
- The code could run, but the event that starts it cannot happen locally: a platform-event subscriber, for instance.
Reproduce: your own coverage report prints these same words.
Which Apex features are supported, one at a time?
| Family | What it covers | Verdict | Caveat |
|---|---|---|---|
async | Queueable, @future, Schedulable, Batchable | EXECUTE | SIMULATED drained inline after commit, not truly deferred; per-chunk transactions and Database.Stateful are not modelled |
callout | HttpRequest / HttpResponse outbound calls | EXECUTE | SIMULATED answered from the mock registry you arm with POST /__emulator__/callouts; unmatched callouts throw System.CalloutException, and nothing reaches the network |
crypto | Crypto.getRandomInteger and friends (seeded) | EXECUTE | SIMULATED seeded and reproducible rather than real entropy, which is the property tests need |
crypto-unshipped | digests, signatures, encryption | UNRESOLVED | — |
database | Database.insert/update/delete partial-success | EXECUTE | Provisional database: SaveResult/Error envelope Provisional (partner-pinned) |
database-savepoint | Database.setSavepoint and rollback | UNRESOLVED | — |
database-unshipped | Database.query and Database.merge | UNRESOLVED | — |
managed-package-code | calling a managed package's Apex | UNRESOLVED | — |
managed-package-objects | a managed package's objects and fields, from your retrieval | EXECUTE | SIMULATED the package's objects are ordinary objects here and its own triggers and validation do not exist, so logic the package would run in a real org does not run |
recorded-effect | EventBus.publish and Messaging.sendEmail | EXECUTE | SIMULATED captured and readable afterwards, never actually sent |
schema | describe served from the org's retrieved metadata | EXECUTE | Provisional schema: describe labels derived where the metadata carries none; DisplayType modeled as its enum name; == identity on describe results Provisional (SCHEMA-DESIGN §7) |
schema-metadata-gated | describe answers the retrieved metadata does not carry | UNRESOLVED | — |
schema-no-metadata | describe for an object no metadata was retrieved for | UNRESOLVED | — |
schema-permission | isAccessible / isCreateable / isUpdateable / isDeletable | EXECUTE | SIMULATED answers as a system-mode user; field-level security is not enforced |
schema-unshipped | describe surfaces outside the shipped set | UNRESOLVED | — |
settings | Custom Setting and Custom Metadata reads | EXECUTE | SIMULATED org defaults only — no per-user or per-profile resolution |
Reproduce: sfapex capabilities
Do these numbers match the binary I would download?
Generated 2026-10-20 from build 27eb778, against image
fidelic/emulator:latest, which reports 27eb778. Shipped
tier: C1.
Reproduce: docker run --rm fidelic/emulator:latest version
How do I run this against my own org?
docker run --rm -v "$PWD/mdapi":/mdapi:ro \
--entrypoint /sfapex fidelic/emulator coverage --report /mdapi How these figures are built: how the numbers are measured.