Skip to main content
In production, the Assertion Enforcer reports assertion failures to the network integration. An enforcing integration can exclude a failing transaction before block inclusion; the executor itself reports all rejections and does not make the block-inclusion decision. In testing, failed assertions cause reverts. This lets you use vm.expectRevert() to verify your assertion catches violations and returns the expected error message. Understanding this difference is important for writing effective tests and interpreting their results.

Quick Comparison

Production: Network Integrations Apply the Result

In production, the Assertion Enforcer validates transactions during block building: Key behaviors:
  • Triggers are evaluated to determine which assertions run
  • All matching assertions for all interacted contracts execute
  • A failing assertion produces a rejection report
  • An enforcing integration can use that report to exclude the transaction before inclusion

Testing: Transactions Revert

In testing, cl.assertion() registers an assertion to run on the next external call: Key behaviors:
  • You specify the assertion function via fnSelector, but triggers still determine if it runs
  • Only the specified assertion runs, not all assertions for the contract
  • Only the next external call is validated, then the registration is consumed
  • Reverts simulate drops - use vm.expectRevert() to test failure cases

The cl.assertion() Mental Model

cl.assertion() works like vm.prank() - it only affects the immediately following external call.

State Persistence

When an assertion passes, state changes persist for the rest of the test. You can assert on updated values and register subsequent assertions.

Common Pitfalls

”Expected 1 assertion to be executed, but 0 were executed”

This error means the assertion didn’t run. Common causes:
  1. External call between cl.assertion() and target - the intervening call consumed the assertion registration
  2. Called function doesn’t match a registered trigger - see below
  3. Target transaction reverts before assertion runs - the protocol function itself fails

Trigger Mismatch

The fnSelector parameter specifies which assertion function to run, but triggers are still evaluated. The assertion only runs if the next external call matches a registered trigger for that assertion function.
To verify trigger behavior against recent real transactions, create a release and review its automatic platform backtest.

Testing Assertions

Testing patterns

Triggers

Production trigger behavior

Backtesting

Review historical findings attached to a platform release

Troubleshooting

Common errors