Skip to content

โœ… SFDX Run Tests โ€‹

This repository implements a unified GitHub composite action to run Salesforce Apex, LWC (Jest) and Flow tests from a single step. Each test type can be toggled on or off, coverage is highlighted in the CLI logs and the GitHub step summary, and the coverage reports are written to the paths expected by quality tools like SonarQube/SonarCloud or Codecov.

Apex tests are enabled by default (they are mandatory in Salesforce); LWC and Flow tests are opt-in.

Usage โ€‹

Run all test types and report to Sonar โ€‹

This streamlines the otherwise separate, manual test steps into one action and produces the coverage reports a subsequent SonarCloud scan consumes:

yaml
# Least-privilege token: this workflow only reads the repository.
permissions:
  contents: read

jobs:
  validation:
    name: Validation
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v7.0.1
        with:
          fetch-depth: 0 # required for the Sonar analysis
          persist-credentials: false # the GITHUB_TOKEN is not needed after the checkout - don't leave it in .git/config

      - name: Select Node Version
        uses: svierk/get-node-version@v1.5.1

      - name: Install Dependencies
        run: npm ci --ignore-scripts

      - name: Install SF CLI
        uses: svierk/sfdx-cli-setup@v1.1.2

      - name: Salesforce Org Login
        uses: svierk/sfdx-login@v1.4.2
        with:
          client-id: ${{ secrets.SFDX_CONSUMER_KEY }}
          jwt-secret-key: ${{ secrets.SFDX_JWT_SECRET_KEY }}
          username: ${{ vars.SFDX_USERNAME }}

      - name: Deploy Metadata
        run: sf project deploy start

      - name: Run Tests
        uses: svierk/sfdx-run-tests@v1.1.0
        with:
          apex: true
          lwc: true
          flow: false

      - name: SonarCloud Scan
        uses: SonarSource/sonarqube-scan-action@v8.2.1
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

LWC tests run locally with Jest, so make sure the Node.js dependencies are installed first (e.g. npm install). Apex and Flow tests run against the authenticated org, so log in (and deploy the metadata) beforehand. When Flow tests are enabled, the required @salesforce/plugin-flow CLI plugin is installed automatically if it is not already present.

In the workflow logs, each test run reports a human-readable result - one line per test plus a run summary - folded into a collapsible section, with a concise pass/fail line that stays visible. Test failures are always printed unfolded (including message and stack trace), so they can be inspected without expanding anything.

Apex tests only (default) โ€‹

yaml
      - name: Run Tests
        uses: svierk/sfdx-run-tests@v1.1.0
        with:
          target-org: ci
          test-level: RunLocalTests

Inputs โ€‹

NameRequiredDefaultDescription
apexnotrueRun Apex tests.
lwcnofalseRun LWC (Jest) unit tests.
flownofalseRun Flow tests.
target-orgnoUsername or alias of the target org (Apex and Flow). Not required if the default org is set.
test-levelnoRunLocalTestsTest level for Apex and Flow: RunLocalTests, RunAllTestsInOrg or RunSpecifiedTests.
code-coveragenotrueCollect code coverage for Apex and Flow tests.
apex-output-dirno./tests/apexDirectory for Apex result and coverage files (contains test-result-codecoverage.json).
flow-output-dirno./tests/flowDirectory for Flow result and coverage files.
lwc-test-commandnonpx sfdx-lwc-jest --coverageCommand used to run the LWC Jest tests with coverage (must write an lcov report). Works on any SFDX project without a specific package.json script; override it to use your own, e.g. npm run test:unit:coverage.
lwc-lcov-pathno./coverage/lcov.infoPath to the lcov report produced by the LWC tests, used to report overall coverage.
waitno33Number of minutes to wait for the Apex and Flow test runs to complete.
api-versionnoOverride the api version used for Apex and Flow test api requests, e.g. 59.0.
step-summarynotrueWrite a result section to the GitHub Actions job summary. Set to false to avoid collisions with a custom workflow summary.

Outputs โ€‹

NameDescription
apex-outcomeOutcome of the Apex test run (Passed/Failed).
apex-coverageOverall Apex code coverage.
lwc-outcomeOutcome of the LWC test run (Passed/Failed).
lwc-coverageOverall LWC line coverage.
flow-outcomeOutcome of the Flow test run (Passed/Failed).
flow-coverageOverall Flow code coverage.

Outputs for a test type that was not run are empty. The step fails if any enabled test type fails, while still writing the summary and outputs.

Coverage reporting (Sonar / Codecov) โ€‹

The action writes coverage reports to standard locations so downstream quality gates can pick them up without extra steps:

Test typeReportMatching Sonar property
Apex<apex-output-dir>/test-result-codecoverage.jsonsonar.apex.coverage.reportPath
LWC<lwc-lcov-path> (./coverage/lcov.info)sonar.javascript.lcov.reportPaths

Example sonar-project.properties snippet:

properties
sonar.javascript.lcov.reportPaths=./coverage/lcov.info
sonar.apex.coverage.reportPath=./tests/apex/test-result-codecoverage.json

The scan step above only needs SONAR_TOKEN (plus SONAR_HOST_URL for a self-hosted SonarQube Server). A GITHUB_TOKEN is not required: pull request decoration is done by the SonarQube Cloud GitHub App, so handing the repository token to a third-party action would widen its access for no benefit.

๐Ÿ” Security & versioning โ€‹

Every uses: reference in the snippets above is pinned to an exact release version, e.g. svierk/sfdx-run-tests@v1.1.0. Do the same in your own pipelines:

  • Never reference a mutable ref such as @main or @v1. It runs whatever code sits behind that branch/tag at run time - with access to your org credentials - so a compromised or rewritten ref would run unnoticed.
  • Good - pin to an exact release tag (@v1.1.0). Readable, concrete, and bumped through reviewed pull requests. This is what this repository itself uses.
  • Strictest - pin to a full-length commit SHA (@a1b2c3dโ€ฆ) with the version as a trailing comment. A SHA can never be re-pointed by the publisher; the cost is readability.
  • Enable Dependabot for github-actions in the repository that holds your pipelines, so those pins are bumped for you instead of silently ageing.

Beyond pinning, the same rules apply to workflows using this action:

  • Least-privilege GITHUB_TOKEN - declare a permissions: block granting only what the workflow needs (contents: read for a validation pipeline).
  • persist-credentials: false on checkout - the token is not written to .git/config, so later steps (SF CLI, third-party actions) cannot reuse it.
  • Secrets travel as secrets - pass them into an action input or expose them as environment variables in shell steps ("$SONAR_TOKEN"), never by interpolating ${{ secrets.* }} into the script itself - that would allow command injection and can leak values into the log. This action follows the same rule internally: every input is handed to the composite script via env: and read as a shell variable.
  • Validate pull requests with pull_request, never pull_request_target - the latter runs with the base repository's secrets, which would let a fork execute its own code against your org.

References โ€‹

Releases โ€‹

Latest release notes can be found on the release page.

License โ€‹

The scripts and documentation in this project are released under the MIT License.


โžก๏ธ Full source & releases: svierk/sfdx-run-tests

Released under the MIT License.