BLOG/DEVELOPER DAILY
THEME
DEVELOPER DAILY

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.

Jun 24, 2026 OrchStep Team 6 minROLE: Backend DeveloperSCALE: Any
RUNNABLE DEMO
Full source for this post: blog/tests-everywhere
VIEW SOURCE

There'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:

orchstep.yml
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 test

That's the laptop command. Now the part that closes the gap — CI runs the exact same thing:

# .github/workflows/ci.yml
- run: orchstep run test

No 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-run

What you actually gained

ConcernScattered scripts + CI YAMLOne OrchStep task
Where checks are definedshell + provider YAMLone orchstep.yml
Local vs CI parity"works on my machine"same orchstep run test
Unit / integration / lintsequential, separate jobsone parallel: block
"Did it really pass?"last exit codeexplicit assert
Result summarygrep three logsone 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

One command for "run the tests" — same on your laptop and in the pipeline. Install the binary and try orchstep run test on yours.

#TESTING#CI#TASK-RUNNER#PARALLEL
Try it in two minutes — one binary, no signup.
curl -fsSL https://orchstep.dev/install.sh | sh

RELATED — DEVELOPER DAILY