continuous-integration-testing

Use when configuring Apex test execution in CI/CD pipelines, choosing test levels for deployments, parsing test results, or troubleshooting code coverage in automated builds. Triggers: 'RunLocalTests', 'RunSpecifiedTests', 'sf project deploy', 'code coverage CI', 'JUnit test results'. NOT for Apex test class design patterns, test data factory architecture, or LWC Jest testing.

Best use case

continuous-integration-testing is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use when configuring Apex test execution in CI/CD pipelines, choosing test levels for deployments, parsing test results, or troubleshooting code coverage in automated builds. Triggers: 'RunLocalTests', 'RunSpecifiedTests', 'sf project deploy', 'code coverage CI', 'JUnit test results'. NOT for Apex test class design patterns, test data factory architecture, or LWC Jest testing.

Teams using continuous-integration-testing should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/continuous-integration-testing/SKILL.md --create-dirs "https://raw.githubusercontent.com/PranavNagrecha/AwesomeSalesforceSkills/main/skills/devops/continuous-integration-testing/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/continuous-integration-testing/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How continuous-integration-testing Compares

Feature / Agentcontinuous-integration-testingStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use when configuring Apex test execution in CI/CD pipelines, choosing test levels for deployments, parsing test results, or troubleshooting code coverage in automated builds. Triggers: 'RunLocalTests', 'RunSpecifiedTests', 'sf project deploy', 'code coverage CI', 'JUnit test results'. NOT for Apex test class design patterns, test data factory architecture, or LWC Jest testing.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# Continuous Integration Testing

Use this skill when Apex tests must run as part of an automated CI/CD pipeline rather than manually through the Developer Console or Setup UI. The objective is a pipeline configuration that executes the right test level, collects accurate coverage numbers, produces machine-readable results, and fails the build when coverage or test outcomes do not meet deployment thresholds.

---

## Before Starting

Gather this context before working on anything in this domain:

- **Which CI platform is in use?** The `sf` CLI commands are the same everywhere, but authentication (JWT, web login, auth URL) and artifact handling differ across GitHub Actions, GitLab CI, Jenkins, and others.
- **Does the target org contain managed packages?** Managed package tests inflate failure risk under `RunAllTestsInOrg` and are skipped under `RunLocalTests`. The wrong choice causes either false failures or silently skipped coverage.
- **What is the minimum coverage gate?** Salesforce enforces 75% org-wide for production deployments, but many teams enforce higher thresholds (80-85%) or per-class minimums that Salesforce does not natively enforce.

---

## Core Concepts

### Test Levels Control What Runs

The `sf project deploy start` and `sf apex run test` commands accept a `--test-level` flag with four values:

| Level | Behavior |
|---|---|
| NoTestRun | Skip tests entirely. Only valid for non-production targets or metadata-only changes. |
| RunSpecifiedTests | Run only the named test classes. Salesforce enforces 75% coverage per class and per trigger in the deployment package — stricter than org-wide. |
| RunLocalTests | Run all tests in the org except managed package tests. Coverage is calculated org-wide at 75%. |
| RunAllTestsInOrg | Run everything including managed package tests. Rarely appropriate in CI because managed package test failures block your deployment. |

The critical nuance: `RunSpecifiedTests` applies coverage requirements per-component in the package, not org-wide. A single under-covered class in your changeset will fail deployment even if the org is at 90% overall.

### Coverage Collection Is Not Coverage Enforcement

The `--code-coverage` flag on `sf apex run test` instructs the platform to collect coverage data and include it in the result payload. It does not enforce any threshold. Your pipeline script must parse the coverage result and fail the build if coverage is below the team standard. Salesforce only enforces the 75% threshold during actual deployment, not during standalone test runs.

### JUnit Output for CI Report Ingestion

Most CI platforms can parse JUnit XML to display test results in their UI. Use `--result-format junit` on `sf apex run test` to produce this format. The output file can be declared as a test artifact in GitHub Actions, GitLab CI, or Jenkins to get per-test pass/fail reporting in the build dashboard.

### Asynchronous Test Execution and Polling

By default, `sf apex run test` with `--synchronous` runs tests in a single synchronous request, which is limited to a smaller set of tests. Without `--synchronous`, tests run asynchronously and the CLI polls for completion when `--wait` is specified. A known platform behavior: combining `--code-coverage` with `--wait` on asynchronous runs can return 0% coverage. The workaround is to let the run complete, then retrieve results separately with `sf apex get test --test-run-id <id> --code-coverage`.

---

## Common Patterns

### Pattern: Two-Stage Deploy with Validation Then Quick-Deploy

**When to use:** Production deployments where you want a CI validation gate before the actual deployment window.

**How it works:**

1. Run `sf project deploy validate --test-level RunLocalTests --target-org prod` during CI. This runs all local tests and validates coverage without actually deploying.
2. Capture the validation ID from the output.
3. During the deployment window, run `sf project deploy quick --job-id <validation-id> --target-org prod` to deploy without re-running tests.

**Why not the alternative:** Running `sf project deploy start` directly means tests and deployment happen atomically. If CI validates in the morning and deployment is approved in the afternoon, validation-then-quick-deploy avoids re-running the full test suite.

### Pattern: RunSpecifiedTests with Explicit Coverage Gate

**When to use:** Large orgs where `RunLocalTests` takes too long (30+ minutes) and the team wants fast CI feedback.

**How it works:**

1. Determine which test classes cover the changed Apex classes. Many teams maintain a mapping file or naming convention (`MyClass` -> `MyClassTest`).
2. Run `sf project deploy start --test-level RunSpecifiedTests --tests MyClassTest,MyServiceTest --target-org target`.
3. Parse the deployment result for per-class coverage. Fail the build if any component in the package is below 75%.

**Why not the alternative:** `RunLocalTests` in a large org can take over an hour. `RunSpecifiedTests` gives targeted feedback in minutes but requires the team to maintain accurate test-to-class mappings.

### Pattern: Standalone Test Run with Separate Coverage Retrieval

**When to use:** When the pipeline needs test results and coverage data but is not performing a deployment (e.g., a PR validation build against a sandbox).

**How it works:**

1. Run `sf apex run test --test-level RunLocalTests --result-format junit --output-dir ./test-results --wait 30 --target-org sandbox`.
2. Run `sf apex get test --test-run-id <id> --code-coverage --result-format json` to retrieve coverage separately.
3. Parse the JSON coverage output and enforce the team threshold.

**Why not the alternative:** Combining `--code-coverage` with `--wait` on the initial run can return 0% coverage due to a known CLI behavior. Separating the retrieval step avoids this.

---

## Decision Guidance

| Situation | Recommended Test Level | Reason |
|---|---|---|
| Production deployment, no managed packages | RunLocalTests | Covers all local code, enforces 75% org-wide |
| Production deployment, managed packages present | RunLocalTests | Avoids failures from managed package tests you cannot control |
| Fast PR validation, known test mapping | RunSpecifiedTests | Minutes instead of hours; per-class 75% is still enforced |
| Sandbox validation, exploratory | RunLocalTests | Broad coverage check without deployment risk |
| Metadata-only change (labels, layouts) | NoTestRun | No Apex involved; tests add no value |
| Full regression before release | RunAllTestsInOrg | Only when you explicitly want to verify managed package compatibility |

---

## Recommended Workflow

Step-by-step instructions for configuring CI testing in a Salesforce pipeline:

1. **Authenticate the CI runner** -- configure JWT-based or auth-URL-based authentication so the pipeline can connect to the target org without interactive login. Store the connected app consumer key and server key as CI secrets.
2. **Choose the test level** -- use the decision table above. Default to `RunLocalTests` for production, `RunSpecifiedTests` for PR builds when test mappings exist, and `NoTestRun` for metadata-only changesets.
3. **Configure the deploy or test command** -- use `sf project deploy validate` for production validation or `sf apex run test` for sandbox-only test runs. Always include `--wait` with a timeout (e.g., `--wait 60`) so the pipeline blocks until tests finish.
4. **Capture results in JUnit format** -- add `--result-format junit --output-dir ./test-results` and publish the output directory as a CI artifact. Most CI platforms auto-detect JUnit XML.
5. **Retrieve coverage separately if needed** -- if using `sf apex run test` with `--code-coverage`, verify coverage numbers are non-zero. If they report 0%, fall back to `sf apex get test --test-run-id <id> --code-coverage` as a second step.
6. **Enforce coverage thresholds in script** -- parse the coverage JSON and fail the build if org-wide or per-class coverage falls below the team-defined minimum. Do not rely solely on Salesforce's 75% deployment gate.
7. **Report results** -- publish JUnit artifacts, log coverage percentages, and surface failures clearly in the CI dashboard.

---

## Review Checklist

Run through these before marking work in this area complete:

- [ ] CI pipeline authenticates to the target org without interactive prompts
- [ ] Test level is explicitly set (not relying on default behavior)
- [ ] `--wait` timeout is long enough for the org's full test suite
- [ ] JUnit XML output is collected and published as a CI artifact
- [ ] Coverage enforcement script parses actual coverage and gates the build
- [ ] The 0% coverage bug workaround is in place if using async test runs with `--code-coverage`
- [ ] Pipeline fails fast on test failures before attempting deployment

---

## Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

1. **`--code-coverage` with `--wait` returns 0%** -- when running asynchronous tests, combining these flags can result in coverage data showing 0% even when tests pass. The platform has not finished aggregating coverage by the time the CLI reads the result. Fix: retrieve coverage separately using `sf apex get test --test-run-id <id> --code-coverage`.
2. **RunSpecifiedTests enforces per-class coverage, not org-wide** -- unlike `RunLocalTests`, which applies the 75% threshold org-wide, `RunSpecifiedTests` requires 75% coverage on every individual class and trigger in the deployment package. A single under-covered class blocks the entire deployment.
3. **Parallel test execution is opt-in per class** -- by default, Salesforce runs test classes serially in a test run. The `@isTest(isParallel=true)` annotation opts a class into parallel execution, which can reduce total run time but introduces risk if tests share mutable state through `SeeAllData` or poorly isolated setup.

---

## Output Artifacts

| Artifact | Description |
|---|---|
| CI pipeline configuration | YAML or Jenkinsfile with correct sf CLI commands, test levels, and coverage gates |
| JUnit XML test results | Machine-readable test output for CI dashboard integration |
| Coverage enforcement script | Shell or Python script that parses coverage JSON and exits non-zero below threshold |

---

## Related Skills

- test-class-standards -- use when the problem is test design quality (assertions, isolation, data factories) rather than CI pipeline configuration
- sf-cli-and-sfdx-essentials -- use for general sf CLI setup, authentication, and project structure questions beyond testing
- batch-job-scheduling-and-monitoring -- relevant when CI tests exercise batch Apex and need async test patterns

Related Skills

scim-provisioning-integration

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when designing or reviewing SCIM-based user lifecycle provisioning into Salesforce from Okta, Azure AD / Entra, or another IdP — create/update/deactivate, group-to-permission-set mapping, attribute mapping, and deprovisioning semantics. Triggers: 'scim provisioning', 'okta scim salesforce', 'entra salesforce provisioning', 'user deactivation automation', 'group to permission set mapping'. NOT for SSO/authentication setup (see single-sign-on skills).

omnistudio-testing-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when testing or validating OmniStudio components — OmniScript preview, Integration Procedure step debugging, DataRaptor field-mapping validation, and end-to-end UTAM-based automation. NOT for Apex unit testing or standard Flow debugging.

omnistudio-lwc-integration

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when embedding OmniScripts in Lightning Web Components, registering custom LWC elements inside OmniScript screens, or calling OmniScript/Integration Procedures from LWC. Triggers: embed omniscript in LWC, custom LWC element in OmniScript, call OmniScript from Lightning page, omnistudio-omni-script tag, seed data JSON, OmniScript launch from LWC. NOT for standalone LWC development, standard Flow embedding, or OmniScript-to-OmniScript embedding.

integration-procedures

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when building, reviewing, or debugging OmniStudio Integration Procedures. Triggers: 'integration procedure', 'IP', 'HTTP action', 'DataRaptor', 'rollbackOnError', 'failureResponse'. NOT for Apex-only integrations unless the main design choice is whether OmniStudio is still appropriate.

integration-procedure-cacheable-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when designing Integration Procedures (IPs) with platform cache to cut latency and callout load. Covers cache key design, TTL selection, per-user vs org-wide partitions, invalidation on data changes, and safe fallback on cache miss/stale. Does NOT cover general IP authoring (see omnistudio-error-handling-patterns) or LWC client-side caching.

lwc-testing

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when setting up or reviewing Lightning Web Component unit tests with Jest, including `@salesforce/sfdx-lwc-jest`, wire adapter mocks, imperative Apex mocks, async rerender handling, and accessibility smoke checks. Triggers: 'how do I test @wire in LWC', 'Jest test is flaky', 'mock Apex in LWC test', 'flushPromises pattern'. NOT for Apex unit tests, browser end-to-end automation, or performance testing.

lwc-jest-testing-with-accessibility

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when authoring or reviewing Jest unit tests for Lightning Web Components and the test plan must include explicit accessibility assertions — covers `@salesforce/sfdx-lwc-jest` setup, `createElement` / `document.body.appendChild` render harness, wire-service mocks via `@salesforce/wire-service-jest-util`, imperative Apex mocks via `jest.fn()`, simulated user interactions (`click`, `keydown`, `focus`), ARIA attribute and accessible-name assertions, focus-management tests, keyboard-navigation tests, and optional `axe-core` integration via `jest-axe`. Triggers: 'add a11y assertions to my LWC jest tests', 'how do I test focus management in LWC', 'jest test for keyboard navigation', 'integrate axe-core into sfdx-lwc-jest', 'assert ARIA attributes after interaction', 'how do I prove the LWC is accessible in CI'. NOT for general LWC jest setup without an a11y angle (use lwc/lwc-testing — this skill is the accessibility-deep-dive sibling), NOT for accessibility-pattern authoring inside the component itself (use lwc/lwc-accessibility-patterns), NOT for end-to-end UI automation via UTAM, NOT for manual screen-reader QA workflows.

slack-salesforce-integration-setup

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill when setting up or troubleshooting the Salesforce for Slack managed app — including connecting a Salesforce org to a Slack workspace, configuring the three-party admin handshake, linking Slack channels to Salesforce records, enabling record preview sharing, and managing org-level limits. Triggers on: Salesforce for Slack app not connecting, Slack org connection setup, Salesforce record sharing in Slack, Slack workspace admin approval, connecting Salesforce to Slack. NOT for building custom Slack apps or Slack bots (separate development platform), not for Slack Workflow Builder Salesforce connector (use slack-workflow-builder skill), not for Flow-based Slack messaging (use flow-for-slack skill).

sis-integration-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill when designing or implementing an integration between a Student Information System (SIS) — such as Ellucian Banner, Ellucian Colleague, Anthology Student, Oracle PeopleSoft Campus Solutions, or Workday Student — and Salesforce Education Cloud. Covers the canonical Education Cloud data model objects (AcademicTermEnrollment, CourseOfferingParticipant, CourseOfferingPtcpResult, LearnerProfile, PersonAcademicCredential), external ID / upsert keying strategies using SIS-native identifiers (Banner PIDM, PeopleSoft EMPLID), batch nightly upsert patterns, Change Data Capture (CDC) for enrollment status writeback, and MuleSoft/middleware watermark patterns. Trigger keywords: SIS integration, Banner integration, PeopleSoft integration, Education Cloud data model, enrollment sync, grade writeback, AcademicTermEnrollment, LearnerProfile upsert. NOT for Salesforce Admissions Connect application processing, Financial Aid integration, Learning Management System (LMS) integrations, or general ETL tooling not involving Education Cloud objects.

salesforce-to-salesforce-integration

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill to implement Salesforce-to-Salesforce integration patterns — covering the native S2S feature, API-based cross-org sync, Platform Event bridging, and Salesforce Connect Cross-Org adapter. Trigger keywords: Salesforce to Salesforce integration, cross-org data sharing, S2S feature, cross-org Platform Events, Salesforce Connect cross-org. NOT for multi-org strategy or architecture decisions (use architect/multi-org-strategy), single-org data sharing, or external (non-Salesforce) system integration.

real-time-vs-batch-integration

8
from PranavNagrecha/AwesomeSalesforceSkills

When to use this skill: choosing between real-time (synchronous callouts, Platform Events, CDC, Pub/Sub API) and batch (Bulk API 2.0, scheduled ETL) integration patterns. Trigger keywords: should I use real-time or batch, how to sync high-volume data, when to use Platform Events vs Bulk API, integration latency vs volume tradeoff. NOT for Batch Apex internals (use batch-apex-patterns), NOT for MuleSoft middleware design (use middleware-integration-patterns), NOT for CDC field tracking configuration.

platform-events-integration

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when publishing Platform Events from external systems via REST API, subscribing to Platform Events from outside Salesforce via CometD or Pub/Sub API, designing replay ID strategy for durable external consumers, or handling high-volume event delivery guarantees. Trigger keywords: 'external publish platform event', 'CometD subscribe', 'Pub/Sub API', 'replay ID external', 'durable subscription', 'RetainUntilDate'. NOT for Apex-only event publishing or triggering (use platform-events-apex). NOT for Change Data Capture external subscription (use change-data-capture-integration).