API Testing

Salesforce Bulk API Testing

Salesforce Bulk API testing validates the high-volume jobs that load, update, delete and extract records at scale — confirming that every row was processed, that failed rows are captured and explained, and that the counts in Salesforce reconcile with the source file. SyntraFlow is designed to run Bulk API 2.0 ingest and query jobs as test steps and assert on job state, per-row results and the data that actually landed.

Bulk API validation is available for demonstration and proof-of-concept validation against your org and load files.

Why bulk loads fail quietly

A Bulk API job is not one transaction. When a CSV is uploaded to a Bulk API 2.0 ingest job, Salesforce splits the data into internal chunks and processes them asynchronously, running triggers, validation rules, duplicate rules and record-triggered Flows against each chunk. A job can reach the JobComplete state while hundreds of individual rows were rejected — and JobComplete only means Salesforce finished processing, not that every record succeeded.

That is why bulk defects rarely show up as errors in the calling system. The middleware or ETL tool logs a completed job, moves on, and the gap between the rows in the source file and the records in the org only appears weeks later in a finance report or a failed downstream sync. Bulk API testing closes that gap by treating numberRecordsProcessed, numberRecordsFailed and the failed-results file as first-class assertions rather than log noise.

This page covers the Bulk API specifically. For the per-record, synchronous calls an integration makes, see Salesforce REST API testing; for the wider API discipline, see the Salesforce API testing hub.

Anatomy of a Bulk API 2.0 ingest test

SyntraFlow can be configured to drive each stage of the job lifecycle and assert at every transition, not just at the end.

1

Prepare a known load file

A CSV with controlled rows is generated — mostly valid records plus deliberately invalid ones such as a missing required field, a bad picklist value or a duplicate external ID — so the expected success and failure counts are known before the job starts.

2

Create the job with explicit settings

The job is opened with the object, the operation (insert, update, upsert, delete or hardDelete), the external ID field for upserts, and the column delimiter and line ending, and the test confirms the job returns in the Open state.

3

Upload data and close the job

The CSV is uploaded and the job state is set to UploadComplete. The test checks that a malformed header or an unknown field name is rejected at this stage rather than surfacing later as row failures.

4

Poll until a terminal state

Job status is polled with a sensible interval and timeout until it reaches JobComplete, Failed or Aborted, so the assertion runs on a settled job instead of guessing how long processing takes.

5

Reconcile the counts

numberRecordsProcessed and numberRecordsFailed are compared with the expected numbers derived from the load file, and any rows returned as unprocessed are flagged as a defect in their own right.

6

Read successful and failed results row by row

The successful results are matched to source rows by sf__Id and the failed results are checked for the exact sf__Error expected for each seeded bad row, so an unexpected error code fails the test.

7

Query back what landed

A follow-up query samples or fully compares the created and updated records, proving field values survived triggers and Flows and that upserts matched the right existing records.

Bulk scenarios and what each one proves

A useful bulk suite deliberately covers the operations and data shapes that behave differently at volume.

ScenarioWhat it proves
Insert of a clean fileThe baseline path works and processed counts equal source row counts with zero failures.
Upsert on an external IDExisting records are updated rather than duplicated, new ones are created, and a duplicate external ID within the file is reported, not silently merged.
Seeded validation failuresRows breaking a validation rule, required field or picklist restriction fail individually with the expected error, while valid rows in the same chunk still commit.
Parent and child loadsChild rows that reference a parent by external ID resolve correctly, and children whose parent failed are reported rather than orphaned.
Delete versus hardDeleteSoft-deleted records move to the Recycle Bin, hard deletes do not, and the integration user holds the Bulk API Hard Delete permission only where intended.
Trigger-heavy objectsApex triggers and record-triggered Flows run correctly in chunks of 200 records without hitting CPU or SOQL governor limits.
Row-lock contentionLoads that touch shared parent records surface UNABLE_TO_LOCK_ROW failures and the retry strategy reprocesses only the affected rows.
Query and queryAll extractA query job returns every expected row across paged result sets using the locator, and queryAll includes soft-deleted and archived records where required.

Bulk-specific failure modes to design for

These defects are characteristic of asynchronous, chunked processing and rarely appear in single-record testing.

Success that is not success

A JobComplete status with a non-zero numberRecordsFailed is treated as a pass by many pipelines. Tests should fail when failures exceed the expected count.

Unread failed results

Failed rows are only visible in the failed-results file. If the integration never downloads it, rejected records vanish without a trace.

Encoding and delimiter drift

A change to line endings, a stray comma in an unquoted field or a non-UTF-8 character shifts columns and loads values into the wrong fields.

Automation at volume

Logic that works for one record can breach governor limits when a chunk of 200 fires a trigger, a Flow and a rollup together.

Limit exhaustion

Large or repeated loads consume the rolling 24-hour record and batch allocations, so a rerun during a busy window can fail for reasons unrelated to data.

Partial reruns that duplicate

Reloading a whole file after a partial failure creates duplicates unless the job uses upsert on a stable external ID.

Limits and settings worth asserting

Bulk API behavior depends on org allocations and job configuration, so a thorough suite checks them explicitly rather than assuming defaults.

  • The rolling 24-hour Bulk API record and batch allocations shown in Setup and the limits resource, checked before large test runs so a limit failure is not mistaken for a data defect.
  • The upload size of each ingest job, with large files split deliberately so no single upload exceeds the documented maximum.
  • The operation and externalIdFieldName on every job, so an upsert is never accidentally run as an insert.
  • The API version used to create jobs, because job resources and behavior can differ between versions and an integration pinned to an old version may miss fixes.
  • Integration user permissions — API Enabled, object and field access, and Bulk API Hard Delete — validated alongside integration user testing.
  • Timeout and polling settings in the calling tool, so long-running jobs are awaited rather than abandoned mid-processing.
  • Whether Bulk API 1.0 jobs remain in use, since serial versus parallel concurrency mode changes lock-contention behavior and needs separate coverage.

Bulk API testing as reconciliation

The real question behind every bulk load is whether the org now matches the source. Counts are the first signal, but a matching count can still hide a wrong value — a currency field loaded into the wrong column, or an owner lookup that resolved to a default user. SyntraFlow is designed to compare loaded records field by field against the source file or the upstream system, so a load is judged on the data it produced rather than the status it reported.

That makes Bulk API testing a natural companion to data reconciliation testing, which covers the broader job of proving two systems agree. It also matters during a Salesforce migration, where Bulk API is typically the engine that moves legacy data, and during sandbox seeding, where a failed chunk leaves test environments inconsistent.

Because failed rows are reported rather than swallowed, a bulk test also produces useful evidence: which records failed, why, and whether a targeted rerun of only those rows succeeded.

Bulk API 2.0 versus REST for the same job

Choosing the wrong API for a volume is itself a defect. Tests should confirm the integration uses the right one.

ConsiderationBulk API 2.0REST and sObject Collections
Processing modelAsynchronous job, processed in internal chunksSynchronous request and response
Typical volumeThousands to millions of recordsSingle records or up to 200 per collection call
Failure reportingFailed-results file per jobPer-record results in the response body
What to assertJob state, processed and failed counts, result filesStatus codes, response body, allOrNone behavior
Limit consumptionBulk record and batch allocationsDaily API request allocation per call

Related pages

Salesforce API Testing

The parent hub covering REST, SOAP and Bulk API validation together.

Salesforce REST API Testing

Synchronous record, composite and collection calls with per-request assertions.

Data Reconciliation Testing

Prove the loaded records match the source system field by field.

Integration Error and Recovery Testing

Retry failed rows safely without creating duplicates.

Salesforce Migration Testing

Validate legacy data moved into Salesforce at volume.

Sandbox Seeding

Load consistent test data into sandboxes without silent gaps.

Bulk API Testing testing FAQs

What is Salesforce Bulk API testing?

Salesforce Bulk API testing validates high-volume ingest and query jobs. It checks that jobs reach the expected state, that processed and failed counts match the source, that each failed row carries the expected error, and that the records that landed hold the right values. It focuses on the asynchronous, chunked behavior that single-record API tests never exercise.

Why can a Bulk API job complete while records still fail?

JobComplete means Salesforce finished processing the job, not that every row succeeded. Rows rejected by validation rules, triggers, duplicate rules or lock contention are counted in numberRecordsFailed and listed in the failed-results file. A test that only checks job state will pass while data is missing.

How should a test handle failed rows?

Seed the load file with known bad rows so the expected failures are defined in advance, then read the failed results and assert each row failed for the expected reason. Any additional failure is a defect. A rerun should then target only the failed rows, ideally using upsert on an external ID so reprocessing cannot create duplicates.

Does Bulk API testing cover query jobs as well as loads?

Yes. Query and queryAll jobs are used for extracts that feed data warehouses and downstream systems. Tests confirm that all pages of results are retrieved using the locator, that row counts match an equivalent count query, and that queryAll includes soft-deleted records only where the integration expects them.

Do Bulk API loads fire triggers and Flows?

Yes. Apex triggers, record-triggered Flows, validation rules and duplicate rules run against bulk loads, with triggers processing records in chunks of 200. That is why bulk testing is also a practical way to find automation that breaches governor limits at volume.

Can SyntraFlow run Bulk API jobs inside a larger test?

SyntraFlow is designed to create, upload, close and poll Bulk API 2.0 jobs as steps in a scenario, then assert on counts, result files and the records in the org. Coverage for your specific objects, file formats and load tools is confirmed during a proof of concept.

Trust every row of your bulk loads

See how SyntraFlow is designed to run Bulk API jobs, read every failed row and reconcile what landed against the source.