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| Option | Description |
|---|---|
--legacy | Use the legacy test runner instead of the enhanced one |
--analyze | Run the test analyzer after tests complete, regardless of pass/fail |
--dry-run | Preview without running (also works with --lwc) |
--lwc | Run 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. |
--logic | Run 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-coverage | For --logic: retrieve code-coverage results |
--wait <minutes> | For --logic: streaming wait timeout in whole minutes, ≥ 1 (default 30; invalid values are rejected) |
--allow-zero-tests | For --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, …)| Option | Description |
|---|---|
--api67 | API 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-hints | Advisory check — flags @IsTest classes with no @IsTest(testFor=...) annotation (invisible to Spring ‘26 RunRelevantTests and smart-deploy’s annotation-aware widening) |
--apexguru | Run only the ApexGuru org-side analysis check (honours --json). License/edition-gated — degrades to skipped, never an error |
--skip-apexguru | Skip the additive ApexGuru check during analyzer runs |
--org <alias> | Target org for the ApexGuru check (default: config.defaultOrg) |
--include-fixes | Ask 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) |
--tests | Run the test analyzer only |
--all | Run both the code and test analyzers |
--fix-plan | Send analysis output to AI for a prioritized, file-specific fix plan |
--generate-stubs | Generate @IsTest stub classes for Apex without a test class |
--dry-run | Preview --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| Option | Description |
|---|---|
--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) |
--notify | Dispatch an agent-test-success / agent-test-failure notification through configured channels |
--pr-comment | Post 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| Option | Description |
|---|---|
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> / --all | Whose 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-body | Stop 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| Option | Description |
|---|---|
--threshold <pct> | Fail (exit 1) if org-wide coverage is below this percent (default 75) |
--org <alias> | Target org (defaults to config.defaultOrg) |
--json | Structured 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| Option | Description |
|---|---|
--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 / Option | Description |
|---|---|
<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