aer
Getting Started with aer

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.

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.xml

Run your first test sweep from the project root:

cd path/to/ossc
aer test force-app/main/default

When you do not pass a schema file, aer test builds one 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. Prefer a reusable schema file? Generate one once and pass it to later commands:

aer schema import force-app/main/default --schema build/schema.gob

You can inspect that saved schema with commands such as aer schema inspect objects --schema build/schema.gob. Disable automatic generation with --no-auto-schema if you want to rely exclusively on a saved file.

Working with a legacy Metadata API layout? Point aer at the src/ directory instead—the tooling handles both formats:

# Classic metadata format
aer test src

The same automatic schema generation applies to metadata-format projects as well.

The 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
mkdir -p build
aer schema import force-app/main/default --schema build/schema.gob

aer package create \
  --schema build/schema.gob \
  --output ../ossc/packages/oslog.pkg \
  force-app/main/default

cd ../ossc
aer test force-app/main/default --package packages/oslog.pkg
  • Add --schema build/schema.gob to the final aer test command if you generate a reusable schema for your application and want to pin tests to that metadata.
  • aer package create runs inside the oslog repository and uses its own build/schema.gob, so the package metadata 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/src for metadata projects.
  • Add more packages with additional --package flags or keep everything under packages/ and point to the directory:
aer test force-app/main/default --package-dir ./packages

Load 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.pkg

Want 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 ./packages

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 OrgWideEmailAddress

Leave --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 (in-memory or --db) and never modifies the source file.

Reuse a persistent aer database

If you previously created a database with aer storage create (or from an earlier test run that persisted --db), you can point aer test at the same file:

aer test force-app/main/default \
  --db storage/aer.db

The --db flag should reference a database created by aer so the schema matches what the runtime expects. Tests share a single database connection, so aer runs them serially in this mode; expect longer runtimes compared to the default in-memory execution.

Create and sync a database with aer storage

When you need to build the database yourself, aer storage helps you create and refresh it:

# Create the database using your saved schema
aer storage create \
  --schema build/schema.gob \
  --db storage/aer.db

# Import setup data from an org (works with Force CLI, sf, or sfdx sessions)
aer storage sync \
  --db storage/aer.db \
  --schema build/schema.gob \
  --account your.name@example.com \
  OrgWideEmailAddress User PermissionSetAssignment

# Run tests against the shared database
aer test force-app/main/default \
  --db storage/aer.db
  • aer storage create ensures the SQLite schema matches your Apex metadata; add package flags if you need to pull in managed fields.
  • aer storage sync copies rows from the target org so you can rehearse full workflows locally. Pass object API names as positional arguments after the flags.
  • Passing --db to aer test reuses the same records across runs. Tests run serially when --db is set, so budget extra time for large suites.
  • Pair --bootstrap-db with either approach when you need to layer in data from another SQLite export; omit both --db and --bootstrap-db to fall back to fast, in-memory execution when you do not need shared state.

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.

© 2012–2025 October Swimmer.