ApexUnitTestClassShouldHaveAsserts / ApexBadCrypto / Code Analyzer violations blocking deploy
Salesforce's open-source code scanners (PMD-Apex, Salesforce Code Analyzer) found rule violations in your codebase. They don't block deploys by themselves but most CI pipelines configure them as gates. Each violation has a specific fix.
Also seen asApexUnitTestClassShouldHaveAsserts·ApexBadCrypto·PMD violations·Code Analyzer violations
The Salesforce ecosystem has two recommended scanners:
| Tool | Coverage |
|---|---|
| PMD-Apex | Apex syntax/style rules (named after the venerable Java PMD) |
| Salesforce Code Analyzer | Wraps PMD + adds platform-specific rules (FLS checks, SOQL injection, etc.) |
Both are open-source. Most production CI pipelines run them as a gate.
The most-flagged rules and their fixes
ApexUnitTestClassShouldHaveAsserts
A test method has no System.assert* calls. The test runs but verifies nothing.
Fix: add real assertions:
@isTest static void deposit_increasesBalance() {
Account a = [SELECT Id FROM Account LIMIT 1];
Bank.deposit(a.Id, 100);
a = [SELECT Balance__c FROM Account WHERE Id = :a.Id];
System.assertEquals(100, a.Balance__c, 'Balance should increase');
}
ApexCRUDViolation
Apex DML or query without checking object accessibility.
Fix: use WITH USER_MODE (modern):
List<Account> rows = [SELECT Id, Name FROM Account WITH USER_MODE LIMIT 10];
Or Schema.sObjectType.Account.isAccessible() checks on older API versions.
ApexSOQLInjection
String-concatenated SOQL with user input. See the SOQL injection page.
Fix: bind variables or String.escapeSingleQuotes.
ApexBadCrypto
Hard-coded encryption key or use of weak algorithms.
Fix: use Crypto.generateAesKey(256) for keys; never hard-code; use AES, not DES; use HMAC-SHA-256, not SHA-1.
ApexHardcodedConfiguration
URLs, emails, IDs hard-coded as Apex string literals.
Fix: move to Custom Metadata Types or Custom Settings:
List<API_Endpoint__mdt> endpoints = [SELECT URL__c FROM API_Endpoint__mdt LIMIT 1];
This makes the value configurable per environment without code changes.
ApexAssertionsShouldIncludeMessage
System.assertEquals(100, b) without a message.
Fix: add the third argument:
System.assertEquals(100, b, 'Balance should be $100 after deposit');
The message helps the next developer triage failures faster.
ApexDoc
Public methods missing documentation comments.
Fix: add ApexDoc comments:
/**
* Deposits the given amount into the user's account.
* @param accountId Salesforce Id of the account
* @param amount Decimal amount to deposit (must be > 0)
*/
public static void deposit(Id accountId, Decimal amount) { ... }
Suppress sparingly
For genuinely-OK violations, use a single-line suppress:
@SuppressWarnings('PMD.ApexAssertionsShouldIncludeMessage')
@isTest static void quickSmokeTest() { ... }
Don't suppress a rule org-wide unless you've reviewed every instance. Per-line suppress is auditable; org-wide suppress hides real issues.
Configuring scanner severity
In your pmd-ruleset.xml or .code-analyzer-config.json, set thresholds:
- Error — fail CI
- Warning — surface in PR review
- Info — log but don't block
A reasonable default: errors for security rules (CRUD, FLS, injection, crypto), warnings for style (assertions, docs). Tune based on your team's velocity vs hygiene tradeoff.
Run before pushing
Add a pre-commit hook or npm test script that runs the analyzer locally:
sf scanner run --target force-app/ --format json --severity-threshold 3
Catches issues before they're a PR problem. Most CI pipelines also run the scanner as part of the deploy validation step — same scanner, just centralized.
