Run your tests the same way everywhere
The way your tests run on your laptop should be the way they run in CI — same command, same steps, same result. Here's one OrchStep task that does unit, integration, and lint in parallel, then asserts on the outcome.
blog/tests-everywhereThere's a special kind of broken build: the one where tests pass on your machine and fail in CI. You run go test ./... locally. CI runs go test ./..., then a separate integration job with a -tags=integration flag you forgot existed, then a lint step configured in a YAML file you've never opened. Three different definitions of "the tests passed", and only one of them lives where you can see it.
The fix isn't more YAML in your CI provider. It's one task — orchstep run test — that defines all the checks in one readable place, and gets called identically from your terminal and your pipeline.
One task, three checks, in parallel
Unit tests, integration tests, and lint don't depend on each other, so they shouldn't run one after another. A parallel: block runs them concurrently and merges every child's outputs back into the normal steps.* namespace, so a later step can read each result:
name: ci
defaults:
pkg: "./..."
tasks:
# `orchstep run test` — the same command locally and in CI
test:
desc: "Run unit + integration + lint, then assert they passed"
steps:
- name: checks
parallel:
- name: unit
func: shell
do: echo "go test {{ vars.pkg }}"
outputs:
ok: "true"
- name: integration
func: shell
do: echo "go test -tags=integration {{ vars.pkg }}"
outputs:
ok: "true"
- name: lint
func: shell
do: echo "golangci-lint run"
outputs:
ok: "true"
- name: verify
func: assert
args:
condition: '{{ and (eq steps.unit.ok "true") (eq steps.integration.ok "true") }}'
desc: "unit and integration tests must pass"
- name: report
func: shell
do: echo "green — unit={{ steps.unit.ok }} integration={{ steps.integration.ok }} lint={{ steps.lint.ok }}"(The demo echos the commands so it runs anywhere — swap in your real go test and golangci-lint invocations to make it live.)
What each piece buys you
The assert step is the important one. A bare shell script "passes" if the last command exited zero; everything before it could have quietly failed. Here the build is green only when the condition is true, and the failure message tells you exactly which gate didn't hold. No more reading three job logs to find the one red line.
The parallel: block is the speed. Three checks that took 90s sequentially overlap into the slowest single one. And because each child writes an outputs: value, the report step can summarize the whole run on one line.
Run it
orchstep run testThat's the laptop command. Now the part that closes the gap — CI runs the exact same thing:
# .github/workflows/ci.yml
- run: orchstep run testNo second definition of the test matrix. No flag that exists only in the pipeline. If you can run it locally, you've run what CI runs. To see the resolved plan before executing — useful when you're editing the task — add --dry-run:
orchstep run test --dry-runWhat you actually gained
| Concern | Scattered scripts + CI YAML | One OrchStep task |
|---|---|---|
| Where checks are defined | shell + provider YAML | one orchstep.yml |
| Local vs CI parity | "works on my machine" | same orchstep run test |
| Unit / integration / lint | sequential, separate jobs | one parallel: block |
| "Did it really pass?" | last exit code | explicit assert |
| Result summary | grep three logs | one report line |
Where OrchStep is not the answer
If a single go test ./... is genuinely all your project needs, keep it — don't wrap one command in a workflow for its own sake. This earns its keep the moment "running the tests" means more than one thing, or the moment local and CI start drifting apart.
Where to go next
- Quick Start — your first workflow in two minutes
- Composition — parallel blocks and calling tasks from tasks
- assert — fail fast on the condition that matters
- Variables & Outputs — how
steps.*andoutputs:connect
One command for "run the tests" — same on your laptop and in the pipeline. Install the binary and try orchstep run test on yours.
curl -fsSL https://orchstep.dev/install.sh | sh