Skip to Content
CLICommand ReferenceTesting & Quality

Testing & Quality


sfdt test

Runs Apex tests against the configured org using the enhanced test runner. If tests fail and AI is enabled, sfdt offers to analyze the failures. Results land in logs/test-results/ and the dashboard.

sfdt test sfdt test --analyze sfdt test --legacy sfdt test --dry-run sfdt test --class-names AccountServiceTest,ContactTriggerTest # run only these classes sfdt test --logic # Apex + Flow tests in one pass sfdt test --logic --tests FooTest,FlowTesting.MyFlow --code-coverage sfdt test --lwc # run the project's LWC (Jest) unit tests
OptionDescription
--legacyUse the legacy test runner instead of the enhanced one
--analyzeRun the test analyzer after tests complete, regardless of pass/fail
--dry-runPreview without running (also works with --lwc)
--lwcRun the project’s LWC (Jest) unit tests locally. Prefers npm run test:unit, falls back to the sfdx-lwc-jest binary
--class-names <list>Run only these Apex test classes (comma-separated), overriding the configured testConfig.testClasses for this run. Powers the VS Code “Run test class” CodeLens.
--logicRun Apex and Flow tests together via sf logic run test (Salesforce Spring ‘26 beta). Requires the org “View All Data” permission. Waits for async results.
--org <alias>For --logic: target org (default config.defaultOrg)
--test-level <level>For --logic: RunLocalTests | RunAllTestsInOrg | RunSpecifiedTests
--tests <list>For --logic: comma-separated names — Apex classes and Flow tests as FlowTesting.<name>
--category <cat>For --logic: restrict to Apex or Flow
--code-coverageFor --logic: retrieve code-coverage results
--wait <minutes>For --logic: streaming wait timeout in whole minutes, ≥ 1 (default 30; invalid values are rejected)
--allow-zero-testsFor --logic: exit 0 even when Salesforce runs zero tests. By default a zero-test “pass” exits 1 — it verified nothing (typo’d names, missing FlowTesting. prefix, or a permissions gap)

AI on failure: when tests fail and AI is available, sfdt prompts to analyze the failures — it checks for missing test data, SOQL governor-limit violations, null-pointer exceptions, and assertion failures, then suggests specific fixes. Set features.ai: false to skip the prompt.

--logic is a thin pass-through to Salesforce’s Spring ‘26 sf logic run test, which runs Apex and Flow tests in a single async run. On failure (with features.ai enabled) sfdt offers the same AI failure analysis as the Apex runner, feeding it the captured logic-test output.

--lwc detection. The project’s package.json must wire a runner (an @salesforce/sfdx-lwc-jest — or legacy sfdx-lwc-jest — dependency, or a test:unit script) and at least one lwc/*/__tests__/ directory must exist under the package directories. When not detected, the command exits with a clear, actionable error. The scratch-org CI templates run these same tests when a Jest setup is detected.


sfdt quality

Static code-quality analysis with optional AI fix plan. Can analyze code structure, test quality, or both.

sfdt quality # code analyzer + additive ApexGuru org-side check (default) sfdt quality --api67 # API v67 (Summer '26) user-mode readiness scan only sfdt quality --test-hints # flag @IsTest classes lacking @IsTest(testFor=...) hints sfdt quality --apexguru # ApexGuru org-side analysis only sfdt quality --tests # test analyzer only sfdt quality --all # both analyzers (+ ApexGuru) sfdt quality --skip-apexguru # analyzer run without the org-side check sfdt quality --fix-plan # analyzer + AI fix plan sfdt quality --generate-stubs # generate @IsTest stubs for untested Apex sfdt quality --generate-stubs --dry-run # preview stubs without writing files sfdt quality --output-file sfdt-quality.sarif # extra Code Analyzer v5 output file (SARIF, HTML, CSV, …)
OptionDescription
--api67API v67 (Summer ‘26 user-mode-by-default) readiness scan of local Apex — flags WITH SECURITY_ENFORCED (no longer compiles at v67), classes with no sharing declaration, and without sharing classes doing DML/SOQL. Exits 1 only when blocking errors exist and sourceApiVersion ≥ 67.
--test-hintsAdvisory check — flags @IsTest classes with no @IsTest(testFor=...) annotation (invisible to Spring ‘26 RunRelevantTests and smart-deploy’s annotation-aware widening)
--apexguruRun only the ApexGuru org-side analysis check (honours --json). License/edition-gated — degrades to skipped, never an error
--skip-apexguruSkip the additive ApexGuru check during analyzer runs
--org <alias>Target org for the ApexGuru check (default: config.defaultOrg)
--include-fixesAsk Code Analyzer v5 for actionable fixes/suggestions in the scan output (--include-fixes --include-suggestions); the richer output feeds --fix-plan
--output-file <path>Ask Code Analyzer v5 for a second output file whose format follows the extension — .sarif for GitHub code scanning, .html, .csv, etc. (as supported by v5). The stdout/JSON contract is unchanged (the flag adds a second --output-file to the same scan)
--testsRun the test analyzer only
--allRun both the code and test analyzers
--fix-planSend analysis output to AI for a prioritized, file-specific fix plan
--generate-stubsGenerate @IsTest stub classes for Apex without a test class
--dry-runPreview --generate-stubs output without writing files

Code Analyzer v5 is the only supported engine. The code scan runs Salesforce Code Analyzer v5 (sf code-analyzer run — a just-in-time plugin, PMD 7 / ESLint / RetireJS, that auto-installs on a modern sf CLI). Without v5 the scan reports SKIPPED (never a fabricated clean result). Install v5 manually with sf plugins install code-analyzer if needed. Support for the retired v4 (sf scanner run) — and its --allow-legacy-analyzer opt-in — was removed at 1.0.

ApexGuru org-side analysis (additive). Alongside the local v5 scan, sfdt quality submits your largest non-test Apex classes (up to 10) to ApexGuru — Salesforce’s org-side performance/anti-pattern service — through the org REST API. ApexGuru is license/edition-gated and must be enabled by an org admin, so the check degrades to skipped (loudly — never a fabricated pass) when there’s no org, no license, or the feature is off, and to warn when analysis can’t complete; it never reports an error. It is advisory: the quality exit code stays whatever the local analyzers produced. Results feed the --fix-plan context and persist to logs/apexguru-latest.json (trended in sfdt history as type apexguru).

The AI fix plan groups issues by severity (critical / high / medium / low) with file locations and concrete suggestions, focused on Salesforce concerns: governor limits, CRUD/FLS, bulk-safe patterns, and coverage gaps.

The generated GitHub PR-validation workflow uses --output-file automatically — quality --output-file sfdt-quality.sarif plus github/codeql-action/upload-sarif@v3 puts scan results in the repo’s Security → Code scanning tab.


sfdt agent-test

Runs an Agentforce agent test (sf agent test run — an AiEvaluationDefinition) as a CI gate. Pass/fail is taken from the CLI’s exit code (the reliable signal, like sf apex run test), so it slots into any pipeline. Optionally notifies configured channels and decorates the current PR.

sfdt agent-test --spec MyAgentEval sfdt agent-test --spec MyAgentEval --org uat --wait 45 sfdt agent-test --spec MyAgentEval --notify --pr-comment
OptionDescription
--spec <apiName>Required. Agent test API name (an AiEvaluationDefinition) to run
--org <alias>Target org (default: config.defaultOrg)
--wait <minutes>Wait timeout (default 30; the underlying command is async and sfdt waits for the result)
--notifyDispatch an agent-test-success / agent-test-failure notification through configured channels
--pr-commentPost the pass/fail result to the current PR (via the gh CLI)

A numeric pass-rate threshold (e.g. fail below 90%) is a planned follow-up — it depends on the sf agent test JSON result schema. For now the gate is exit-code based: any failed test case fails the run.


sfdt apex

Apex observability: manage debug trace flags, retrieve and watch debug logs, and execute Anonymous Apex — the debugging loop that complements sfdt test (which owns test execution). Debug logs go through the sf apex commands; trace flags use the Tooling API, since the sf CLI has no first-class trace-flag command. Every subcommand supports --org <alias> and --json.

# Trace flags (mutating — they write TraceFlag records) sfdt apex trace start # trace the authenticated user for 60 minutes sfdt apex trace start --user u@x.com --duration 30 sfdt apex trace list # read-only sfdt apex trace stop # delete the authenticated user's USER_DEBUG flags sfdt apex trace stop --all # delete every USER_DEBUG flag in the org # Debug logs (read-only) sfdt apex logs list --limit 10 sfdt apex logs get 07L5g00000AbCdEEAV --output debug.log sfdt apex logs watch # tail new logs for 5 minutes (CI-safe default) sfdt apex logs watch --duration 0 # until interrupted (interactive) # Anonymous Apex (mutating — the code runs in the org) sfdt apex run --file scripts/apex/reset-flags.apex echo 'System.debug(UserInfo.getUserName());' | sfdt apex run
OptionDescription
trace start --user <username>Username to trace (default: the org’s authenticated user)
trace start --duration <minutes>Trace window in minutes (default 60, capped at 1440 — the Salesforce 24 h limit)
trace start --level <developerName>DebugLevel DeveloperName. Default: the sfdt-managed SFDT_Trace, created on demand; any other missing name is an error — sfdt never silently invents a level you named
trace stop --user <username> / --allWhose USER_DEBUG flags to delete (default: the authenticated user), or all of them
logs list --limit <n> / --user <name>Cap the list (default 20) / only logs from one user
logs get <logId> --output <file>Write the raw log body to a file (the file stays raw; the JSON envelope is stdout-only)
logs watch --interval <seconds>Poll interval (default 5)
logs watch --duration <seconds>Total watch window (default 300; 0 = until interrupted). Bounded by default, so it is safe in CI
logs watch --max <n> / --no-bodyStop after n new logs / report metadata without fetching bodies
run --file <path>Apex file to execute; without it, code is read from stdin (piped input)

apex run reports the full compile/execution diagnostics (compiled, compileProblem, exceptionMessage, stack trace, and the debug log) and exits non-zero when the Apex failed — in --json mode the envelope still carries the diagnostics, so CI can branch on result.success. apex logs watch streams only logs generated after it starts.

Trace-flag changes and Anonymous Apex mutate the org — their MCP counterparts (sfdt_apex_trace, sfdt_apex_run) require confirmExecution: true, while sfdt_apex_logs is read-only. If the sf apex plugin is unavailable, commands fail with an actionable install hint (sf plugins install @salesforce/plugin-apex) rather than a fabricated result.


sfdt coverage

Reports Apex code coverage from the Tooling API — an org-wide percentage plus a per-class breakdown. Exits non-zero when org-wide coverage falls below --threshold, so it works as a CI gate. Coverage bands are shared with the Chrome extension and dashboard via @sfdt/flow-core.

sfdt coverage # org-wide + per-class, default 75% gate sfdt coverage --threshold 85 # fail under 85% sfdt coverage --org production --json
OptionDescription
--threshold <pct>Fail (exit 1) if org-wide coverage is below this percent (default 75)
--org <alias>Target org (defaults to config.defaultOrg)
--jsonStructured output

Coverage reflects the last test run in the org — run sfdt test first if the org-wide figure shows as unknown. The text output lists the worst-covered classes below threshold first.


sfdt changelog

Manages changelog files. Three subcommands. All accept --package <name> to scope to a package changelog (changelogs/<name>.md); without it, they operate on the global CHANGELOG.md.

sfdt changelog generate

Uses AI to analyze recent git commits and generate [Unreleased] entries, categorized into Added / Changed / Fixed / Deprecated / Removed / Security.

sfdt changelog generate sfdt changelog generate --limit 30 sfdt changelog generate --package marketing
OptionDescription
--limit <n>Number of commits to analyze (default 20)
--package <name>Scope to a package changelog

Requires features.ai: true.

sfdt changelog release

Moves the [Unreleased] section to a new versioned section stamped with the current date. Edits in place; does not commit.

sfdt changelog release 1.5.0 sfdt changelog release 1.5.0 --package marketing
Argument / OptionDescription
<version>Semver version (X.Y.Z)
--package <name>Target a package changelog

sfdt changelog check

Validates that the changelog is in sync with git state — warns if you have uncommitted changes but an empty [Unreleased]. Exits 1 if the changelog needs updating, so it works as a CI gate.

sfdt changelog check sfdt changelog check --package marketing
Last updated on