Salesforce Interview Prep

πŸ“„ TOPIC 6 β€” ANSWER SHEET (SEALED)

TESTING & TEST CLASSES β€” MODEL ANSWERS

06b_Topic06_Testing_TestClasses_Answer_Sheet.md

SEALED β€” the answers are the property of the learner who earned them. Do not read a section until you have WRITTEN your own answer (Incident, REDO, or Rapid-Fire) on paper or in a scratch file. The seal breaks once per section, in order. Read the workbook first; answer from memory; then compare. Where your answer differs, rewrite yours β€” the writing is the learning. The sheet answers: every INCIDENT (model answer), every REDO, every Retrieval Drill, the Capstone (model report + 3 regression tests + 2-minute script), all 20 Rapid-Fire, the 5 Interleaved Practice problems, and the One-Card Key.


M0 β€” THE MODULE MAP (MODEL SUMMARY)

  • 1. The Green Build That Deployed a Bug β€” coverage β‰  behavior; assertions must prove the code's promise on re-queried state. The coverage-farming org and the retry logic that broke in production.
  • 2. The Test That Failed on Fridays β€” determinism: no calendar dependence (fixed dates), no shared static state (reset in @TestSetup). The Friday-only failure and the serial number that leaked.
  • 3. The Callout Test That Called the Internet β€” mocks are mandatory: HttpCalloutMock/WebServiceMock/static-resource mocks via Test.setMock; success AND failure paths (500/timeout) are both testable β€” the mock owns the conversation.
  • 4. The 40-Minute Test That Deployed Nothing β€” three failures: overweight fixture (bulk = code paths, not rows), missing startTest/stopTest (the window flushes async synchronously), asserting the fixture instead of the batch's effect.
  • 5. The Async Test That Tested Nothing β€” async work enqueued outside the window runs after the test: green-but-untested. Rule: enqueue inside startTest; assert after stopTest. Module 3 tie-ins: callout mock + retry/dead-letter assertions.
  • 6. The runAs Test That Ran as the Wrong User β€” tests run in system context by default; sharing/FLS tests require System.runAs(user) (real user, standard profile). Assert the user's view + FLS-stripped fields.
  • 7. The Scheduled Job That Never Fired β€” Test.getCronTrigger proves the schedule row, not the execution; scheduled jobs fire inside the window. Two legs: mechanics + behavior.
  • 8. The Exception Test That Swallowed the Bug β€” try/catch with no assertion = worse than no test; expectedExceptions for throwing code, behavior assertions (retry counter/dead-letter) for handling code.
  • 9. πŸ† Capstone β€” The Suite That Deployed the Org β€” the testing contract: behavior claims, re-queried state, user's context, async window, mocked callouts, no swallowed exceptions; 4 audit answers + 4-component checklist + 3 regression tests.

The contract sentence (memorize): "Every test asserts a behavior claim the code promises β€” measured on re-queried state, under the user's context, inside the async window, with mocks for every callout, and never a swallowed exception."


INCIDENT 1 β€” MODEL ANSWER

What the broken assertion proved vs what the behavior requires: the catch(Exception e) proved only "no exception escaped" β€” a process claim. The sync contract promises a state change: invoices with Synced__c = false become Synced__c = true after the queueable runs. Coverage counted the lines executed (the run() call and its catch), not the behavior (the status field updated). The suite's "95%" was line coverage of a process assertion.

The corrected test: re-query the records after Test.stopTest() (the window flushes the queueable) and assert the exact promised state:

  • System.assertEquals(2, [SELECT COUNT() FROM Invoice__c WHERE Synced__c = true]) β€” the exact count of synced rows;
  • assert the specific records (inv.Id, inv2.Id in the synced set) β€” the promise kept on the right rows;
  • assert the failed path's retry counter (Retry_Count__c = 1) β€” the failure behavior, not just the success. If the code had used Security.stripInaccessible / WITH USER_MODE, the test also asserts the stripped shape (see Incident 6).

Coverage is a byproduct: the deploy threshold is 75%+ as a floor; a 95% process-assertion suite is worthless, a 65% behavior suite ships. In an interview: "Coverage proves the code ran; the assertion proves the promise."

INCIDENT 1 β€” RETRIEVAL DRILL ANSWERS

  1. What does the green build + broken production prove? β€” Coverage β‰  behavior; the suite asserted process (no exception), the contract was a state change (Synced flag). Line coverage counted execution, not the promise.
  2. The assertion formula. β€” The promised state change, re-queried, exact values, on the right records, success AND failure paths.
  3. "Process assertion" vs "behavior assertion" β€” define each with an example. β€” Process: "no exception / record exists / code ran" (the broken catch test). Behavior: "the invoice row is Synced after the flush" (the corrected test).
  4. How to re-query and assert an updated record. β€” [SELECT Status__c FROM Invoice__c WHERE Id = :inv.Id] after Test.stopTest(); assert the exact field value; assert exact counts on the affected set.
  5. How would you present coverage in an interview? β€” "Coverage proves the code ran; assertions prove the promise. I treat 75% as the floor and behavior coverage as the metric."

INCIDENT 2 β€” MODEL ANSWER

The two determinism killers (name them as a pair): (1) Calendar dependence β€” Date.today(), System.now(), DateTime.now() computed at test time: the test's expected values must not depend on when the test runs (the Monday check failed only on Fridays; the invoice-due calculation failed only when a month boundary fell between setup and assertion). (2) Shared static state β€” @TestSetup runs once per class, and static variables are not reset between test methods by default: a System.serialNumber counter incremented in setup leaked into the second test's assertions (the serial the test expected β‰  the serial the setup consumed).

The fixes: (1) Fixed date in @TestSetup: Date fixedDate = Date.newInstance(2026, 8, 20); β€” the setup creates invoices with Due_Date__c = fixedDate, the test computes expectations from fixedDate, and the assertion System.assertEquals(fixedDate.addDays(1), ...) is time-independent (a fixed reference date also makes tests deterministic across orgs β€” no timezone variance). (2) Static-state reset: initialize the static System.serialNumber in each @TestSetup/test (or use @TestSetup for data and reset statics in each test method); a clean-slate static guarantee: System.resetSerialNumber() in setup β€” the class under test must expose reset or the test creates it fresh.

The Friday test explained: the calendar check System.assertEquals('Monday', dayOfWeek) depended on the run date; on Fridays it failed, and the fix (fixed reference date, never today()) made the test deterministic.

INCIDENT 2 β€” RETRIEVAL DRILL ANSWERS

  1. The two determinism killers. β€” Calendar dependence (today()/now() in tests) and shared static state (statics not reset between methods).
  2. Why does Date.today() break a test? β€” The expected values move with the run date β€” timezone, DST, and month boundaries make the same test fail or pass by when it runs.
  3. The setup fix for calendar dependence. β€” A fixed reference date (Date.newInstance(2026, 8, 20)); compute expectations from it; assert against it. Tests become time-independent.
  4. Why does @TestSetup make static state worse? β€” Setup runs once per class and statics are not reset per method β€” the second test inherits the first's static residue.
  5. The System.serialNumber fix. β€” Reset the static in each test/setup (a resetSerialNumber method on the class or fresh initialization per method) β€” the same test, same result, every run.

INCIDENT 3 β€” MODEL ANSWER

The mocking rule (state as a law): Tests must never perform real callouts. The engine enforces it: Http.send without a registered mock throws CalloutException β€” and in the "worse" case (permissive environment), the test actually hits the real endpoint and mutates real external state (the 100 real records marked "synced"). Test.setMock(HttpCalloutMock.class, mock) is mandatory registration; the mock owns the entire conversation (request AND response), including the failures.

The mock classes (all four): (1) HttpCalloutMock β€” interface: HTTPResponse respond(HTTPRequest req); the test implements it and returns whatever the scenario needs (200, 500, empty body). (2) StaticResourceCalloutMock β€” returns a static resource body (canned large payloads). (3) MultiStaticResourceCalloutMock β€” different static-resource bodies per endpoint. (4) WebServiceMock β€” SOAP callouts. All registered via Test.setMock.

The success-path test (with request assertions β€” the senior detail): set the mock (200 + body), run the sync inside the window, assert the result and the conversation:

@isTest
static void testSyncSuccess() {
    Test.setMock(HttpCalloutMock.class, new SuccessMock());  // 200 + body
    Test.startTest(); BillingSync.run(); Test.stopTest();
    System.assertEquals('Synced', [SELECT Status__c FROM Invoice__c WHERE Id = :inv.Id].Status__c);
    // the mock captured the request β€” assert endpoint/method/headers the code actually sent:
    System.assertEquals('https://billing.example.com/api/invoices', SuccessMock.capturedEndpoint);
    System.assertEquals('POST', SuccessMock.capturedMethod);
}

The failure-path test (the one that matters): the mock returns 500 (provider error) or throws CalloutException in respond (the timeout simulation β€” Module 3 Incident 8's 3:00 AM blip). Assert the retry behavior: Retry_Count__c = 1 (counter incremented), the retry queueable flushed by stopTest, and on the 3rd attempt the dead-letter Error_Log__c row + alert. The failure test is the resilience test β€” the one that would have caught the Module 3 blackout.

INCIDENT 3 β€” RETRIEVAL DRILL ANSWERS

  1. Why are real callouts forbidden β€” and what does the engine do? β€” Real calls mutate external state and are environment-dependent; the engine throws CalloutException when Http.send runs unmocked β€” the platform's mock-mandate.
  2. The four mock classes + registration. β€” HttpCalloutMock, StaticResourceCalloutMock, MultiStaticResourceCalloutMock, WebServiceMock; all via Test.setMock.
  3. What should the success test assert beyond "no exception"? β€” The promised state (Status = Synced) AND the conversation (endpoint, method, headers the code sent).
  4. Simulate a timeout with a mock. β€” A mock whose respond throws CalloutException (or returns 500).
  5. Which Module 3 incident does the failure test protect? β€” Incident 8's blackout (the 3:00 AM external outage + retry/dead-letter design).

INCIDENT 4 β€” MODEL ANSWER

The three design failures (the verdict):

  1. Fixture volume as a substitute for semantics β€” 50,000 DML calls to "look bulk"; bulk semantics (QueryLocator iteration, chunking, governor limits inside execute) are exercised by a small fixture. Volume adds runtime + flakiness.
  2. Missing startTest/stopTest β€” the batch enqueued outside the window runs truly async: the test can't reliably assert it, and the suite hits the deploy-timeout ceiling.
  3. Asserting the fixture, not the effect β€” the rollup values were pre-set by the fixture; the assertion validated the fixture. The test must compare the batch's computed result to an independent expectation (a manual SUM).

The startTest/stopTest semantics (memorize): Test.startTest() resets governor limits and opens the async window; Test.stopTest() executes all async work enqueued inside the window synchronously β€” futures, queueables, batches (including finish()), scheduled jobs. Enqueue inside; assert after.

The corrected bulk test:

@isTest
static void testRollupComputes() {
    Account p = new Account(Name='P'); insert p;
    insert new List<Child__c>{ new Child__c(Parent__c=p.Id, Amount__c=100),
                               new Child__c(Parent__c=p.Id, Amount__c=200) };
    Test.startTest();
    Database.executeBatch(new RollupBatch(), 200);
    Test.stopTest();                          // batch + finish() run synchronously
    Account result = [SELECT Rollup__c FROM Account WHERE Id = :p.Id];
    System.assertEquals(300, result.Rollup__c);   // the batch's effect vs manual SUM
}

"Testing bulk" without a mountain of rows: a second small test asserts the batch's limit behavior within execute (Limits.getDMLRows()), plus chunking via a scope-size parameter β€” the code paths, verified.

INCIDENT 4 β€” RETRIEVAL DRILL ANSWERS

  1. The three design failures. β€” Overweight fixture (volume β‰  semantics), missing startTest/stopTest (batch ran async/unobserved), asserting the fixture's pre-set values.
  2. What does startTest do β€” and what does stopTest flush? β€” startTest resets governor limits + opens the window; stopTest executes async work enqueued inside synchronously (futures, queueables, batches + finish, scheduled).
  3. Does the batch's finish() run in the test? β€” Yes β€” when the batch is enqueued inside the window, stopTest runs it to completion, finish() included.
  4. "Testing bulk" = testing what? β€” The bulk code paths: QueryLocator iteration, chunking/scope, governor limits inside execute β€” not row volume.
  5. The corrected assertion compares what vs what? β€” The batch's computed Rollup__c (re-queried) vs an independent manual SUM.

INCIDENT 5 β€” MODEL ANSWER

The async timing rules (the #1 async-testing question): async work enqueued outside the window executes after the test method completes β€” the test only observes the pre-execution world (the "0 synced" tell). Enqueued inside the window, Test.stopTest() flushes it synchronously: the queueable's execute runs before stopTest returns, and everything it chains (nested enqueues to the depth limit) runs too. The rule: enqueue inside startTest; assert after stopTest β€” applies to @future (invoke inside), Database.executeBatch (+ finish), System.enqueueJob, System.schedule, and platform-event-triggered flows.

The two failure modes: (1) executes-after-test β€” the work runs post-test; the test asserted pre-execution state and passed while verifying nothing (the org's 92% covered the enqueue line, not the sync); (2) never-observable β€” the work's effects land where no assertion can see them; production is the first run.

The corrected test (+ Module 3 tie-ins):

@isTest
static void testSyncSuccess() {
    Test.setMock(HttpCalloutMock.class, new SuccessMock());   // I3: mock mandatory
    Test.startTest();
    BillingSync.enqueue(new List<Id>{ inv.Id, inv2.Id });
    Test.stopTest();                            // queueable executes NOW
    System.assertEquals(2, [SELECT COUNT() FROM Invoice__c WHERE Synced__c = true]);
}
@isTest
static void testSyncTimeoutRetries() {
    Test.setMock(HttpCalloutMock.class, new TimeoutMock());   // throws CalloutException
    Test.startTest(); BillingSync.enqueue(ids); Test.stopTest();
    System.assertEquals(1, [SELECT Retry_Count__c FROM Invoice__c WHERE Id = :inv.Id].Retry_Count__c);
    // 3rd attempt: dead-letter Error_Log__c row (Module 3 Incident 8 contract)
}

INCIDENT 5 β€” RETRIEVAL DRILL ANSWERS

  1. When does async work enqueued outside the window execute? β€” After the test method completes β€” unobservable; the test asserts a world where the work hasn't run.
  2. The one-line rule for testing async code. β€” Enqueue inside startTest; assert after stopTest.
  3. What does the "0 synced" assertion reveal? β€” The queueable hadn't run yet β€” the test verified nothing (green-but-untested).
  4. The Module 3 tie-ins (name both). β€” The callout mock (Test.setMock) and the retry/dead-letter assertions (TimeoutMock β†’ Retry_Count = 1; max attempts β†’ Error_Log__c row).
  5. Does stopTest flush chained queueables? β€” Yes β€” everything enqueued inside the window, including chained queueables, executes synchronously at stopTest (to the depth limit).

INCIDENT 6 β€” MODEL ANSWER

The default-context trap: tests execute as the test-running user β€” system context, where sharing and FLS are not enforced. A security test without runAs asserts the admin's view β€” green and meaningless for the security behavior. System.runAs(user) { ... } runs the block under the given user's context: sharing rules, FLS, and permissions apply exactly as in production.

The constraints (know them): the runAs user must be a real user record created in the test; give it a standard profile (not system admin) and any needed PermissionSets; create it before runAs. runAs does NOT change: license limits for system operations, and it doesn't bypass test isolation.

The corrected security test (4 steps):

@isTest
static void testRepSeesOnlyOwned() {
    User repA = TestUtil.createRep();            // 1: standard-profile user
    Account mine  = new Account(Name='Acme-1', OwnerId = repA.Id); insert mine;   // 2: controlled ownership
    Account others = new Account(Name='Acme-2', OwnerId = otherUser.Id); insert others;
    System.runAs(repA) {                          // 3: runAs block
        List<Account> r = AccountSearch.find('Acme');
        System.assertEquals(1, r.size());            // 4: assert the USER's view
        System.assertEquals('Acme-1', r[0].Name);
    }
}

The FLS assertion pattern (Module 5 Incident 2, as a test): inside runAs(repA), assert the sensitive fields are stripped: System.assertEquals(null, result.SSN__c) (the Security.stripInaccessible behavior) β€” or assert the WITH USER_MODE filtered shape. Security tests assert the user's result, not the code's result.

INCIDENT 6 β€” RETRIEVAL DRILL ANSWERS

  1. Who executes test code by default β€” and what does that hide? β€” The test-running user in system context; sharing and FLS behavior is invisible.
  2. What must a runAs user have (3)? β€” Real user record created in the test, standard profile (not admin), needed PermissionSets.
  3. What does runAs NOT change? β€” License limits (some system ops still run system-side) and test isolation (real data still invisible).
  4. The 4-step corrected security test. β€” Create standard-profile user β†’ insert controlled-ownership records β†’ System.runAs(user) { ... } β†’ assert the user's view (counts + specific records).
  5. How do you assert FLS behavior? β€” Inside runAs: sensitive fields null (stripInaccessible) or the USER_MODE filtered shape.

INCIDENT 7 β€” MODEL ANSWER

The false-proof diagnosis: Test.getCronTrigger('Reminder') != null proves the schedule row was created β€” nothing about the job's execute. System.schedule creates the cron entry immediately; execution happens at the scheduled time, outside the test β€” unless scheduled inside the window, where stopTest flushes it synchronously (scheduled work is async work β€” Incidents 4/5's rule). The coverage dashboard showed the schedulable "covered" (the schedule call executed) while execute ran zero times in tests β€” production night 1 was the first run.

The correct pattern (memorize):

@isTest
static void testReminderSchedulesAndFires() {
    Test.startTest();
    String jobId = System.schedule('Reminder', '0 0 2 * * ?', new ReminderJob());
    Test.stopTest();                       // scheduled job executes NOW
    System.assert(Test.getCronTrigger(jobId) != null);   // mechanics: the schedule exists
    System.assertEquals(1, [SELECT COUNT() FROM EmailMessage ...]);  // behavior: the job's effect
}

Assert on the returned jobId, not the name (names can collide).

The two-leg design (checklist): Leg 1 β€” mechanics: cron trigger exists, scheduled at the right time, schedule returned an ID, no duplicate schedules. Leg 2 β€” behavior: execute runs (window flush or direct new ReminderJob().execute(null) call) and the effect is asserted (emails/records). If the job calls out (Module 3's nightly sync), leg 2 needs Test.setMock β€” the mock requirement travels everywhere (Incident 3).

INCIDENT 7 β€” RETRIEVAL DRILL ANSWERS

  1. What does Test.getCronTrigger actually prove? β€” The schedule row exists β€” not that the job ran; existence β‰  execution.
  2. When does a scheduled job execute in a test? β€” Only when scheduled inside the startTest/stopTest window β€” stopTest flushes it synchronously.
  3. Why assert on jobId instead of the name? β€” Names can collide; the returned ID uniquely identifies the trigger.
  4. The two legs of a schedulable test. β€” Leg 1 mechanics (cron exists, timing, no duplicates); Leg 2 behavior (execute runs + effect asserted).
  5. Which Module does the mock requirement tie back to? β€” Module 3 (integrations): callout-mandatory mocking (Incident 3).

INCIDENT 8 β€” MODEL ANSWER

The diagnosis (three reasons β€” memorize): (1) No assertion β€” the test body ended inside the catch; green meant "the runner saw no failure," not "the behavior is correct." (2) Exception-swallowing β€” the test's own try/catch intercepted the exception; the test cannot distinguish "the code crashed" from "the code handled the failure" β€” both look identical from inside the catch. (3) Asserting the wrong thing β€” "an exception was thrown" is the opposite of the retry contract (the code must catch, increment, enqueue). The implicit expectation describes the broken behavior; the retry path was never exercised.

The two correct negative-test patterns (know when each applies):

  1. @isTest(expectedExceptions = SomeException.class) β€” code that SHOULD throw (validation, guard clauses, malformed input): the annotation IS the assertion β€” the test fails if no exception is thrown. The throwing pattern.
  2. Behavior assertions β€” code that should HANDLE a failure (retries, dead-letter, fallback): run it (in the window if async), then assert the handling state. The handling pattern β€” the retry case is a handling test, not a throwing test.

The corrected retry test (the full Module 3 Incident 8 contract):

@isTest
static void testRetryOnTimeout() {
    Test.setMock(HttpCalloutMock.class, new TimeoutMock());   // throws CalloutException
    Test.startTest();
    BillingSync.run();                       // code should CATCH and retry
    Test.stopTest();
    System.assertEquals(1, [SELECT Retry_Count__c FROM Invoice__c WHERE Id = :inv.Id].Retry_Count__c);
    // max-attempt variant (3 failures): assert dead-letter Error_Log__c row + alert
}
// Pattern 2 (code SHOULD throw):
@isTest(expectedExceptions = CalloutException.class)
static void testInvalidInputThrows() {
    BillingSync.run(badInput);               // must throw β€” the annotation asserts it
}

Related handling test: Database.insert(records, false) β€” assert the per-record SaveResult errors (the handling pattern applied to DML).

INCIDENT 8 β€” RETRIEVAL DRILL ANSWERS

  1. The three reasons the swallowing test is worse than none. β€” No assertion (green β‰  correct); exception-swallowing (crashed vs handled are indistinguishable); wrong expectation (throwing is the broken behavior for a retry contract).
  2. Which pattern for code that SHOULD throw β€” and what does the annotation do? β€” @isTest(expectedExceptions=...); it fails the test if no exception is thrown β€” the annotation IS the assertion.
  3. Which pattern for code that should HANDLE β€” what is the assertion? β€” Behavior assertions: the handling state (retry counter, dead-letter, fallback) after the window.
  4. What does the corrected retry test assert (3 clauses)? β€” Retry_Count = 1; the retry queueable executed (window flush); dead-letter Error_Log__c + alert on max attempts.
  5. Why is "test that it throws" the wrong expectation for a retry contract? β€” The contract says the code must catch and handle β€” an escaping exception is the broken behavior; the test must assert the handling.

INCIDENT 9 (CAPSTONE) β€” MODEL ANSWER

1. The 8-pattern recap (the testing contract, one line each):

  • I1: Assert behavior, not process β€” the promised state change, re-queried and exact. Coverage is a byproduct.
  • I2: Tests are deterministic β€” fixed dates, no shared static state; @TestSetup-reset, mocks for callouts.
  • I3: Mock every callout β€” success AND failure; the mock owns the conversation (Test.setMock + the four mock classes).
  • I4: Right-sized fixtures + the window for batches β€” bulk = code paths, not rows.
  • I5: Enqueue inside the window, assert after β€” stopTest is the flush.
  • I6: runAs for sharing/FLS β€” assert the user's view, not the system's.
  • I7: Schedulables have two legs β€” mechanics (cron exists) + behavior (job fires, effect asserted).
  • I8: Negative tests either expect or assert β€” expectedExceptions for throwing, handling assertions for retries; never swallow.

2. The audit answers (test strategy):

  • (a) What must a test assert? β†’ The state change the code promises, re-queried and exact: the sync marks the row Synced, the rollup equals the manual SUM, the rep sees only owned records. Process assertions are symptoms, not behavior.
  • (b) How do you measure testing success? β†’ Behavior coverage per component (success / failure / security / async paths each have a claim), plus suite runtime β€” a 40-minute suite is a design failure.
  • (c) The minimum for a deployable suite? β†’ 75%+ coverage as the floor; behavior tests for every async/security path; no swallowing tests; green + fast.
  • (d) Async and security? β†’ Async: the window β€” enqueue inside, assert after, mocks for callouts. Security: System.runAs + user-view assertions + FLS-stripped fields.

3. The new-hire sprint plan (the checklist with Incident mappings):

  • The queueable β†’ [I5: window] + [I3: mock, both paths] + [I8: timeoutβ†’retryβ†’dead-letter handling].
  • The account search β†’ [I6: runAs sharing β€” owned vs not] + [I6: FLS stripped].
  • The batch rollup β†’ [I4: right-sized + window] + [I1: computed-value vs manual SUM].
  • The scheduled reminder β†’ [I7: mechanics leg] + [I7: behavior leg] + [I3: mock if it calls out]. Every checklist ends with: "Which Incident patterns does this component violate if untested?"

4. The three regression tests that MUST ship:

  1. Queueable success + timeout β€” proves the sync marks rows Synced after the flush (I5+I3) and the timeout path increments the retry counter and dead-letters on max attempts (I8) β€” the Module 3 resilience contract.
  2. Search under runAs β€” proves the rep sees only owned records and FLS strips sensitive fields (I6) β€” the Module 5 security contract.
  3. Batch + schedule window β€” proves the rollup equals the manual SUM (I4+I1) and the scheduled job fires inside the window with its effect asserted (I7) β€” the automation contracts.

Why "obvious fixes" failed: more tests = volume without the contract; coverage dashboards = reporting, not discipline; one big test class = no structure β€” the per-component, per-pattern checklist is the structure.

INCIDENT 9 β€” RETRIEVAL DRILL ANSWERS

  1. The testing contract in one sentence. β€” "Every test asserts a behavior claim the code promises β€” measured on re-queried state, under the user's context, inside the async window, with mocks for every callout, and never a swallowed exception."
  2. All 8 patterns in order. β€” I1 behavior-not-process; I2 deterministic; I3 mock both paths; I4 right-sized + window; I5 enqueue-inside-assert-after; I6 runAs; I7 two legs; I8 expect-or-assert.
  3. The 4 audit answers (compressed). β€” Assertions = promised state changes; success = behavior covered per component + fast; minimum = 75% floor + behavior-complete + no swallowing; async/security = window + runAs.
  4. Pattern maps: queueable β†’ 5+3+8; search β†’ 6; batch β†’ 4+1; scheduled β†’ 7+3.
  5. The three regression tests + proofs. β€” Queueable success+timeout (synced rows + retry/dead-letter); search runAs (owned-only + FLS); batch+schedule (manual-SUM rollup + job fires).

RAPID-FIRE β€” MODEL ANSWERS (20)

  1. The test asserts the fixture, not the behavior β€” process assertion (T1); re-query the code's result.
  2. "Coverage is the floor, not the goal" β€” find the behavior gaps (success/failure/security/async) and assert them; don't farm lines.
  3. Calendar dependence β€” the test computes expectations from today(); fix with a fixed reference date (T3).
  4. CalloutException β€” the platform enforces the mock rule (T4).
  5. SOAP β€” HttpCalloutMock is REST; WebServiceMock is SOAP (T4).
  6. Yes β€” everything enqueued inside the window, batch finish() included, runs at stopTest (T5).
  7. The window β€” enqueue the batch inside startTest/stopTest; outside, it runs after the test (T6).
  8. The bulk code paths β€” QueryLocator iteration, chunking/scope, limits inside execute; not row volume (T6).
  9. The test-running user, system context β€” sharing/FLS invisible; runAs is the fix (T7).
  10. Created in the test β€” real user, standard profile, needed PermissionSets (T7).
  11. Proves the schedule row exists β€” the job executes only via the window; assert the effect after stopTest (T7/T5).
  12. Worse than no test β€” swallows the failure, asserts nothing; either expect or assert (T8).
  13. Code that SHOULD throw β€” validation/guard clauses; the annotation fails the test if nothing throws (T8).
  14. Handling assertion β€” the failure was handled; the retry state IS the assertion (T8).
  15. Governor limits β€” fresh limits for the window (T5).
  16. The failure path β€” retry/dead-letter behavior (T4/T8).
  17. It asserted the admin's view β€” the rep's view was never computed; runAs is the verification (T7).
  18. After β€” the flush happens at stopTest; assert the post-flush state (T5).
  19. The enqueue line, not the behavior β€” coverage counted execution; the sync promise was never asserted (T1).
  20. Schedule inside the window + assert the effect β€” the two legs (T7/T5).

INTERLEAVED PRACTICE β€” MODEL ANSWERS (5)

IP-1 (Integration Γ— Testing): The single test: Test.setMock(HttpCalloutMock.class, new TimeoutMock()) β†’ window β†’ 3 failures β†’ assert Retry_Count__c = 2 on the 2nd attempt and the dead-letter Error_Log__c row on the 3rd, plus the alert record. The mock throws CalloutException in respond (timeout simulation); the assertions prove the retry path increments AND the dead-letter path fires β€” Incidents 3+8. IP-2 (Security Γ— Testing): The fix-proving test: runAs(rep) with an owned account + an account owned by another user β†’ System.assertEquals(1, results.size()) + the owned record's name + System.assertEquals(null, result.SSN__c) (FLS stripped). The pre-fix test asserted the admin's view (system context) β€” green and meaningless β€” Incident 6. IP-3 (Async Γ— Testing): Three findings: (1) 50,000-row fixture = volume, not semantics; (2) no startTest/stopTest β€” the batch ran after the test; (3) assertions compared against fixture pre-set values. Rewrite: 3 parents Γ— 4 children β†’ window β†’ assert Rollup__c vs manual SUM (300) β€” Incident 4. IP-4 (Automation Γ— Testing): The flow's contract ends at its record outcome (the flow test asserts the record state the flow produces); the queueable's contract is the async behavior β€” its test needs the window (I5) + the callout mock if it calls out (I3) + the failure/retry assertions (I8). The two tests compose: flow outcome test + queueable behavior test. IP-5 (Order of Execution Γ— Testing): Behavior tests asserting the final record state after the full chain (re-queried, exact values β€” e.g., after trigger + workflow + flow + process all run, the record holds the composed outcome). Process assertions (no exception, one link at a time) miss the chain's composed result β€” exactly Incident 1's disease at the order-of-execution scale.


THE ONE-CARD KEY (the whole module on one card)

Front: "Testing & Test Classes β€” the contract." Back (3 lines):

  1. Assert behavior, not process β€” the promised state change, re-queried, exact, success + failure paths; coverage is a byproduct. (I1, I8)
  2. The window + the mock + the user β€” enqueue inside startTest, assert after stopTest (I4/I5/I7); Test.setMock owns every callout, both paths (I3); System.runAs for sharing/FLS, assert the user's view (I6).
  3. Deterministic or it doesn't exist β€” fixed dates, reset statics, right-sized fixtures, never a swallowed exception. (I2, I4, I8)

Seal verified. Every model answer above is a deployable claim β€” say them aloud until they are yours.