Docs
Testing

Built-in matchers and helpers

Reference map for transaction predicates, map expectations, and out-action helpers in Acton tests

Before writing custom matchers, check the built-in ones first. Acton already ships a useful set of helpers for transaction search, map assertions, and out-action inspection.

Transaction search matchers

Use these functions to find or assert on transactions from net.send(...):

  • findTransaction()
  • toHaveSuccessfulDeploy()
  • toHaveSuccessfulTx()
  • toHaveTx()
  • toNotHaveTx()
  • toHaveBouncedTx()
  • toHaveFailedTx()

You can mix exact scalar matches and predicate functions inside SearchParams:

expect(res).toHaveTx({
    from: fun (addr: address): bool {
        return addr == deployer.address;
    },
    to: harness.address,
    value: ton("0.5"),
    opcode: 0x0000_0001,
});

Important matcher semantics:

  • SearchParams fields accept either exact values or predicate functions.
  • toHaveSuccessfulTx() forces success = true and defaults exitCode = 0 unless overridden explicitly.
  • toHaveFailedTx() requires a non-zero exitCode.
  • Bounced-message opcode matching should be done with toHaveBouncedTx() or an explicit bounced: true filter.
  • Predicate functions can be useful for custom diagnostics, but runtime errors inside those predicates surface back to the test output.

Go deeper in:

External message matchers

net.sendExternal(...) returns an ExternalSendResult. When the external-in message is accepted, result.transactions contains the produced transaction trace. When the contract rejects the message before acceptance, the trace is null and result.error contains emulator metadata such as externalNotAccepted, message and vmExitCode.

val result = net.sendExternal(msg);

expect(result).toBeAccepted();

val txs = result.unwrap();
expect(txs).toHaveAllSuccessfulTxs();

val tx = result.at(0);
result.giveName("external vote");

Available helpers:

  • isAccepted() returns whether the external-in message produced a trace.
  • unwrap() returns the produced SendResultList or fails with the same external diagnostics as toBeAccepted().
  • at(index) unwraps the trace and returns one SendResult.
  • giveName(name) names the external trace for the Test UI and gas/fee reports.
  • waitForFirstTransaction() waits for the root on-chain transaction, or returns null when the external message was not accepted.
  • waitForTrace() waits for the full on-chain trace, or returns null when the external message was not accepted.
  • findExternalOutMessage<T>() searches external-out messages in the accepted trace, or returns null for rejected external-in messages.

Available matchers:

  • toBeAccepted() expects an accepted external-in message and a produced trace.
  • toBeNotAccepted() expects rejection before acceptance.
  • toHaveExternalVmExitCode(exitCode) checks the VM exit code reported for a rejected external-in message. For accepted messages, use transaction matchers on result.unwrap().

Failed toBeAccepted() checks include the external status, emulator reason, VM exit code when available, known exit-code description or ABI error name, source location, and full backtrace when the test is run with --backtrace full.

val result = net.sendExternal(invalidMsg);

expect(result).toBeNotAccepted();
expect(result).toHaveExternalVmExitCode(10);

Rejected external-in diagnostics include the matcher location and, with --backtrace full, the contract-side onExternalMessage backtrace.

Go deeper in:

Map expectations

Map helpers are useful for get-method results and in-memory fixtures:

val balances: map<int32, int32> = net.runGetMethod(ledger.address, "balances");

expect(balances).toContainKey(1);
expect(balances).toContainValue(20);
expect(balances).toNotContainKey(99);
expect(balances).toNotContainValue(777);
expect(balances).toBeNonEmpty();
expect(balances).toHaveLength(2);

Available helpers:

  • toContainKey()
  • toNotContainKey()
  • toContainValue()
  • toNotContainValue()
  • toBeEmpty()
  • toBeNonEmpty()
  • toHaveLength()

Practical note:

  • toHaveLength() counts the current number of keys after overwrites, not the number of set() calls used to build the map.

Go deeper in:

Out-action helpers

Use action helpers to inspect C5 output, planned sends, or action kinds:

val outActions = testing.outActions();

expect(outActions).toBeSendMessageAt<IncreaseCounter>(2);

val action = outActions.getSendMessageAt(2);
val body = outActions.getSendMessageBodyAt<IncreaseCounter>(2);

The most useful entry points are:

  • testing.outActions() for the current VM out list
  • SendResult.allOutActions() for one executed transaction result
  • OutMessage.outActions(mode) for a message before it is sent
  • OutActionList.at()
  • OutActionList.getSendMessageAt()
  • OutActionList.getSendMessageBodyAt()
  • Expectation<OutActionList>.toBeSendMessageAt()

Important out-action semantics:

  • Out-action indexing is reversed: index 0 is the last produced action.
  • kind() returns stable string tags such as "send-message" and "reserve-currency".
  • getSendMessageAt() and getSendMessageBodyAt() return null when the selected action is not a send-message action.
  • toBeSendMessageAt<Msg>() verifies that the action is a send-message action and that typed message loading succeeds. For body-field assertions, load the message or body separately.

Go deeper in:

When to write custom matchers

If the built-in helpers still do not match a domain-specific vocabulary, extend Expectation<T> with a custom matcher.

Last updated on

On this page