Getting Started with aer
aer lets you execute Apex tests locally without waiting on a Salesforce deploy. This guide walks through a basic project and then layers in package and data dependencies so you can reuse the same workflow across your portfolio.
Install
If you use Homebrew, install aer with:
brew install octoberswimmer/tap/aerYou can also install aer as a Salesforce CLI plugin:
sf plugins install @octoberswimmer/aer-sf-pluginOr install the VS Code extension, which downloads aer for you and adds test running, code coverage, and the interactive debugger to the editor.
Otherwise, download the latest version of aer from the releases page.
Hello World
Run anonymous Apex locally with aer exec:
aer exec <<APEX
Account a = new Account(Name = 'Hello World');
insert a;
Account result = [SELECT Name FROM Account WHERE Id = :a.Id];
System.debug(result.Name);
APEXDEBUG | Hello Worldaer exec executes anonymous Apex in an in-memory environment with standard objects already available, so you can experiment with DML and SOQL without any setup or org connection.
Quick Start: Local Apex Project
Most teams keep their code in Salesforce DX (source) format under force-app/main/default. The example below uses an Apex package named ossc, but the structure matches what you see in any SFDX project:
force-app/
└── main/
└── default/
├── classes/
│ ├── Address.cls
│ ├── Address_Test.cls
│ ├── AgeService.cls
│ └── ...additional Apex classes and tests
└── objects/
└── ShipCompliant_API__c.object
└── fields/
├── CoreService_Endpoint__c.field-meta.xml
├── Password__c.field-meta.xml
└── Username__c.field-meta.xmlRun your first test sweep from the project root:
cd path/to/ossc
aer test force-app/main/defaultaer test builds a schema automatically by scanning the metadata in the directories you supply. The schema lives in memory for that run, so there is nothing to clean up afterward. You can pass multiple source directories when your project splits code across several paths:
aer test force-app/main/default force-app/extra/defaultWorking with a legacy Metadata API layout? Point aer at the src/ directory instead—the tooling handles both formats:
# Classic metadata format
aer test srcThe remaining examples use force-app/main/default for consistency. If your project still uses the classic metadata layout, substitute src in each command.
Load First-Party Packages
Many teams split shared Apex into separate repositories. If you have the source for those dependencies available locally (for example, an oslog logging package that sits next to your ShipCompliant project), convert them into packages once and reuse them across every test run:
cd path/to/ossc
mkdir -p packages
cd ../oslog
aer package create \
--output ../ossc/packages/oslog.pkg \
force-app/main/default
cd ../ossc
aer test force-app/main/default --package packages/oslog.pkgaer package createruns inside theoslogrepository and scans its metadata, so the package lines up with the source code.- Packages load straight from disk—you never need to install them in an org—which keeps integration logic intact.
- Adjust the source path (
../oslog/force-app/main/default) to match your dependency’s layout; point to../oslog/srcfor metadata projects. - Add more packages with additional
--packageflags or keep everything underpackages/and point to the directory:
aer test force-app/main/default --package-dir ./packagesLoad Third-Party Packages
Managed packages you cannot build locally still fit into the workflow. Use aer package mock to snapshot the metadata from an org where the package is already installed. The example below captures the dlrs namespace from a sandbox and runs tests with both first-party and third-party code:
# Export the managed package once
aer package mock dlrs \
--account your.name@example.com \
--output packages/dlrs.pkg
# Run tests with all package dependencies
aer test force-app/main/default \
--package packages/oslog.pkg \
--package packages/dlrs.pkgWant a deeper walkthrough? See Mock Managed Package Classes → for a step-by-step example that stubs oslog.Log inside a mocked package.
When you have several .pkg files, place them in a shared folder and load them in bulk:
aer test force-app/main/default --package-dir ./packagesA .pkg file can also be passed as a source path, or left inside a source directory, with no --package flag:
aer test force-app/main/default fflib.pkgLoaded this way, the package’s contents are treated like source code: they model an unmanaged package whose components belong to the org, take the default namespace rather than the package’s own name, and any test classes inside the package are discovered and run alongside your own tests. Use --package when the package should keep its own namespace.
Each package executes in its own namespace, so components do not conflict unless two archives target the same namespace. Loading multiple packages for a single namespace is not supported today. .pkg files are binary, but they can still be checked into source control so your CI environment can reuse them. The --account flag accepts usernames from both the Force CLI and the newer sf/sfdx authentication stores—use whichever tool already has a session for your sandbox.
Bring In Setup Data
Some tests require seed data such as OrgWideEmailAddress records or reference tables that are not stored in source. aer supports two approaches: point tests at a SQLite database that already contains the records you need, or generate that database on demand and keep it in sync with your org.
Seed from an external SQLite snapshot
When you want to pull in records maintained outside of aer, use --bootstrap-db to copy data from another SQLite file into the test database before execution. For example, capture the OrgWideEmailAddress table with the ro CLI and hand it to aer:
ro -D bootstrap.db OrgWideEmailAddress
aer test force-app/main/default \
--bootstrap-db bootstrap.db \
--bootstrap-tables OrgWideEmailAddressLeave --bootstrap-tables unset to copy every table from the bootstrap database, or repeat --bootstrap-tables for each table you want to include. The bootstrap file is read-only; aer copies the data into its own database and never modifies the source file. When the target is a persistent --db database, the copy merges into it: existing rows are kept, a bootstrap row whose Id already exists is ignored, and re-running the copy is a no-op.
Run a Test Suite
Test suite metadata in testSuites/*.testSuite-meta.xml is loaded with the rest of your source, and --suite runs the classes a suite names:
aer test force-app/main/default --suite BillingRepeat --suite to run several suites in one invocation. The suites are also queryable as ApexTestSuite and TestSuiteMembership records.
Save Your Flags
Flags that every run needs, such as --package-dir, --assign-perms, or --timezone, can be written once to an .aerrc file in the project root instead of being repeated on the command line:
--package-dir packages
--assign-perms My_Permission_Set
--timezone America/ChicagoSee Configuration for the file format and lookup order.
With packages and data dialed in, aer delivers production-faithful Apex tests that run in seconds on your laptop. Extend the setup as your dependencies grow, check in the configuration, and the whole team—including CI—benefits from fast, dependable feedback.
Next Steps
For more details, see the full aer documentation.