Testing Custom Actions
Unit tests give custom Hexabot actions a stable contract before the action is published or reused across projects. A good action test suite should cover the workflow-facing contract, the execution beh
This guide uses Jest examples for reusable hexabot-action-* packages, but the same patterns apply to project-local actions compiled under dist/extensions/actions/**/*.action.js.
What to Test
Test each action at three levels:
Schema contract: valid and invalid
input,settings, andoutputpayloads.Execution behavior: service calls, HTTP requests, SDK calls, memory updates, bindings, and normalized return values.
Failure behavior: missing credentials, validation failures, upstream errors, network errors, retry-sensitive side effects, and safe logging.
Use focused unit tests. Do not bootstrap the full Hexabot API unless the test is explicitly verifying NestJS module wiring.
Install Jest Dependencies
For a standalone TypeScript action package, install Jest with a TypeScript transform. This example uses @swc/jest, which matches the Hexabot monorepo test style.
npm install -D jest @swc/core @swc/jest @types/jest typescriptWith pnpm:
pnpm add -D jest @swc/core @swc/jest @types/jest typescriptAdd scripts to package.json:
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage"
}
}Jest Configuration
Create jest.config.cjs:
If the package uses TypeScript path aliases, add moduleNameMapper entries or avoid aliases in testable action packages.
Test File Layout
Keep tests close to the action source:
Or keep all tests under test/:
Use the layout your package already uses, then make testRegex or testMatch match it.
Instantiate an Action
createAction() returns an injectable action class. For a unit test, instantiate it directly with a mocked ActionService.
When your action extends BaseAction and injects additional services, pass mocks for those constructor arguments:
Test the Schema Contract
Use parseInput, parseSettings, and parseOutput to test the contract that workflow authors rely on.
Schema tests should cover:
required fields;
invalid formats such as email, URL, UUID, and enum values;
cross-field validation rules;
default settings;
the success output shape;
the failure output shape;
rejection of unsupported extra fields when schemas are strict.
Do not redefine timeout_ms or retries in an action settings schema. Hexabot parses those base settings for every action.
Test Execution with Mocked Context Services
Actions receive runtime dependencies through context.services, context.event, context.memoryStore, and bindings. Unit tests can pass the minimum context shape the action needs.
Call execute() when you want to isolate action logic. Call run() when you want the base runtime to validate input, validate output, apply timeout/retry settings, and reject unsupported bindings.
Mock HTTP Calls
Prefer mocking the HTTP client instead of calling external APIs. If the action uses fetch, spy on global.fetch.
If the action uses axios, mock the module:
Test Failure Paths
Failure tests are as important as success tests because workflows often branch on failure outputs.
Also test malformed upstream payloads, network errors, missing required context, and any idempotency behavior for write actions.
Test Memory Actions
For memory actions, mock context.memoryStore.
Validate memory slugs and make sure the output is JSON-serializable.
Test Conversational Actions
Conversational actions usually need context.event. Mock only the methods the action calls.
Test the missing-event path when the action cannot run outside a conversational workflow.
Restrict conversational-only actions with workflowTypes in the action metadata, then test that workflow-specific assumptions are enforced by the action logic.
Test Bindings
When an action consumes runtime bindings, test both the direct execution path and the base runtime validation path.
For custom binding kinds, instantiate the binding provider with a mocked RuntimeBindingsService and assert registration:
Test Safe Logging
Log assertions should prove that useful metadata is logged without leaking secrets.
Avoid snapshots for logs or upstream payloads that may contain credentials or PII.
Run Tests Before Publishing
Run the package checks before publishing or installing the action in a shared project:
With pnpm:
For actions inside the Hexabot monorepo, run the API package tests:
Use a clean Hexabot project for final installation verification, but keep that as a package integration check. Unit tests should stay fast and deterministic.
Checklist
Valid input parses successfully.
Invalid input fails with useful schema errors.
Settings parse with action settings and base
timeout_ms/retries.Success and failure outputs match
outputSchema.External clients are mocked.
Credentials are resolved through
context.services.credentials.Missing or empty credential values are tested.
Non-2xx, network, and malformed upstream responses are tested.
Logs do not include raw secrets or full sensitive payloads.
Memory and conversational context assumptions are tested when relevant.
Binding support is tested when the action consumes bindings.
action.run()is covered when timeout, retry, schema, or binding behavior is part of the contract.
Last updated
Was this helpful?