Test Suite Patterns
The URLs, scripts, and stack names below are placeholders. Replace them with actual project targets. The HTTP and language checks fail on unmet expectations; printing an error without a failing exit status would count as success.
Custom Command with Native HTTP Checks
Define this in atmos.yaml or imported CLI configuration, then run atmos test:
commands:- name: testdescription: Run smoke tests against the deployed applicationsteps:- name: smoke-teststype: testtitle: Post-deployment testsoutput: failuresfail:mode: wait_allsteps:- name: healthtitle: Healthtype: httpurl: https://app.example.com/healthtimeout: 10sexpect:status: [200]response: ['"status"\s*:\s*"ok"']retry:max_attempts: 3delay: 2s- name: homepagetitle: Homepagetype: httpurl: https://app.example.com/expect:status: [200]
expect.response is a list of response-body regexes; at least one must match.
It is not a JSONPath assertion. HTTP retries cover transport errors, 5xx, and 429
by default; consult the HTTP documentation for retry.conditions when another
response should be retried. Use a script for precise JSON assertions.
For a workflow, put the same test step under that workflow's steps and invoke
it through atmos workflow <name> -f <file>.
Parallel Checks with a Dependent Script Assertion
Insert this group as a child of type: test. Homepage can run alongside health;
response validation waits for health. If health fails, validation is skipped.
The health response is .steps.health.value, not a shared temporary file.
- name: endpointstitle: Endpointstype: parallelmax_concurrency: 2steps:- name: healthtitle: Health responsetype: httpurl: https://app.example.com/healthexpect:status: [200]- name: validate-responsetitle: Health payloadtype: scriptneeds: [health]interpreter: python3env:HEALTH_RESPONSE: '{{ .steps.health.value }}'script: |import jsonimport osresponse = json.loads(os.environ["HEALTH_RESPONSE"])if response.get("status") != "ok":raise SystemExit("Expected health status 'ok'")if response.get("ready") is not True:raise SystemExit("Application is not ready")print("Health payload is valid")- name: homepagetitle: Homepagetype: httpurl: https://app.example.com/expect:status: [200]
Declare Python in the owning command/workflow's dependencies.tools if it is
not provided by the project's execution environment. Passing raw response data
through env avoids embedding it as executable Python source.
Matrix Cases with Existing Shell Tests
Insert this as another child of type: test, alongside a parallel group rather
than inside it. Two regions times two stages expand to four leaf cases. The
checked-in script must inspect the target and return nonzero on failure.
- name: regionstitle: Regional checkstype: matrixmatrix:region: [us-east-1, eu-west-1]stage: [dev, staging]max_concurrency: 2steps:- name: smoketitle: Smoke testtype: shellenv:AWS_REGION: '{{ .matrix.region }}'TEST_STAGE: '{{ .matrix.stage }}'command: ./tests/smoke.sh
Use matrix variables in HTTP URLs, script environment variables, or other
supported templated fields too. Keep credentials separate from axis values.
If cases write files, give each combination its own path. A matrix can also use
child needs to sequence dependent checks within each combination.
Preconditions and Other Interpreters
Check prerequisites before the suite when they are setup requirements rather
than cases to report. These two steps belong in the owning command/workflow's
steps list; Node must already be available or declared in dependencies.tools.
- name: prerequisitestype: requiretools: [node]files: [tests/contract.json]- name: contract-teststype: testtitle: Integration contractsteps:- name: contracttype: scriptinterpreter: nodescript: |const fs = require('node:fs');const contract = JSON.parse(fs.readFileSync('tests/contract.json', 'utf8'));if (contract.version !== 1) {throw new Error('Expected contract version 1');}console.log('Contract version is valid');
require can also be a leaf when file/tool/directory presence is itself a test.
Use type: atmos for native checks such as command: validate stacks; a successful
describe command alone does not assert that a deployment is healthy.
Run After Terraform Apply
Place this in the component's hooks configuration using the existing lifecycle
event. The hook envelope contains on_failure; test fields belong under with.
hooks:smoke-tests:events: [after.terraform.apply]kind: steptype: teston_failure: failwith:title: Post-deployment testsoutput: failuresfail:mode: wait_allsteps:- name: healthtitle: Healthtype: httpurl: https://app.example.com/healthexpect:status: [200]
Set on_failure: fail for a deployment gate: step hooks otherwise default to
warning. The same with.steps can contain the parallel and matrix patterns above.
Use the hook skill for component working-directory rules, authentication, and
hook conditions. Do not invent a new deployment trigger or a separate test runner.
Debugging and Failure Policy
For more context, change the group's scalar output to all. To stop remaining
work after a failure, use fail: {mode: fail_fast}. For explicitly advisory checks,
fail: {mode: best_effort} allows a successful group despite red failures.
A leaf's continue: always tolerates that leaf's failure without turning it green.
Preserve these deliberate policies when extending an existing suite.