add a guide on unit testing job code
@mtuchi is already working on this.
Since Aug 18, 2026.
- Dominant language
- JavaScript
- Stars
- 23
- Forks
- 20
- Avg merge
- 3d 1h
- Merged PRs (30d)
- 6
Description
Context
openfn compile now writes compiled job expressions to disk, which means job code can finally be imported into a normal JavaScript test runner. Job expressions are not valid JS out of the box (top-level adaptor calls like get('/endpoint') break
the import), so until now there was no first-class way to unit test the helper functions users write inside their jobs.
There's an internal guide in kit covering the mechanics:
https://github.com/OpenFn/kit/blob/main/.claude/unit-testing-jobs.md
That guide is written for engineers who already know the compiler. We need a user-facing version on docs.openfn.org that is task-oriented: "here is how you test your job code", not "here is what --exports-only does".
Problem
Users writing non-trivial transformation logic (date formatting, SMS parsing, identifier mapping, code lookups) currently have no documented way to test it except by running the whole workflow with the CLI and eyeballing output.json. That's slow, doesn't isolate failures, and doesn't run in CI.
Proposed change
Add a new page: Writing unit tests for your jobs
Suggested location: alongside the job-writing guide, e.g. documentation/jobs/unit-testing-jobs, so it sits with the other "how to write good job code" content rather than buried in the CLI reference.
Cross-links needed from:
documentation/jobs/job-writing-guide— a short "your helpers can be unit tested" pointerdocumentation/cli-usage— addcompileto the common-usage examples with a link heredocumentation/cli— mention testing in the "you can use the CLI to…" list
Content outline
- Why unit test job code — what is and isn't testable. Operations
(fn,get,each) are not unit-testable; pure helper functions are. Set
expectations up front. - Write your helpers so they're testable — the most important section, and
the one missing from the kit guide. In--exports-onlymode only
export constandexport functiondeclarations survive; a plain
const formatDate = ...is dropped. So the guidance is: export any helper
you want to test. Include a before/after snippet. - Compile your workflows —
openfn compile --exports-only, what lands in
dist/, the.mjsextension (and why that means you don't need
"type": "module"), and-o/--clean/--workspace. - Write a test — a full worked example, source → compiled → test, using
Node's built-in test runner (node --test). Reuse theformatDateexample
from the kit guide or write a slightly richer one (e.g. parsing an SMS string
into a structured record) so it demonstrates real value. - The edit → test loop —
--watchalongside the test runner's watch mode. - Wire it into your project —
package.jsonscripts,.gitignorethe
output dir, and a short note on running this in CI (GitHub Actions snippet
would be a nice-to-have, not blocking). - Reference table —
compileflags:--exports-only,-o,-O,
--watch,--clean,--workspace, plus thedirs.compiledkey in
openfn.yaml. - Troubleshooting — see below; this is where most of the new writing is.
Gotchas that must be covered
These are the things that will generate support questions if we skip them:
- "My helper isn't in the compiled file." It wasn't exported. Non-exported
declarations are always dropped in strip mode. - "No file was written for my step." Steps whose compiled output is empty
after stripping are skipped entirely, so the import fails with a
module-not-found error rather than anything descriptive. - Adaptor imports are preserved in compiled output. A step that does
import { dateFns } from '@openfn/language-dhis2'will still carry that import
after compilation, so the adaptor package has to be installed locally for the
test to run. This needs an explicit "install your adaptors as devDependencies"
instruction, or a documented pattern for keeping testable helpers free of
adaptor imports. This is the biggest open gap in the current guide. export defaultis removed in strip mode — expected, since it only exists
for the runtime.- Import paths in tests point at the compiled output (
../dist/...), not the
source. Worth stating plainly, and worth noting that the compiled dir should be
gitignored so tests depend on a build step. - Full compilation (no
--exports-only) writes every step and keeps operations
inexport default [...]. Mention it briefly so users know the flag matters,
but don't make it the focus.
Acceptance criteria
- New page published under
documentation/and added to the sidebar - Page includes a complete, copy-pasteable worked example (source → compiled → test → command to run it)
- All CLI commands and flags in the page have been run against the current released
@openfn/cliand produce the documented output - The adaptor-dependency gotcha is documented with a working solution, not just a warning
- All eight gotchas above are covered somewhere on the page
- Cross-links added from the job-writing guide and CLI usage pages
- Minimum required
@openfn/cliversion stated on the page
Out of scope
- Integration/end-to-end testing of whole workflows (running with fixture state,
asserting on final state). Worth a follow-up page; keep this one focused on
unit tests. - Mocking adaptor operations or HTTP calls.
- Recommending a specific third-party test runner. Use
node --testin examples
and note that any runner works.
Open questions
- Which
@openfn/cliversion first shippedcompilewith--exports-only? Needs
confirming against the kit release notes / changelog before the page states a
minimum version — I don't want to guess at that. - Does the compiled output dir get a
.gitignoreautomatically (as.cli-cache
does), or does the user need to add it? Affects the "wire it into your project"
section. - Should
compilealso get a proper entry in the CLI reference docs as part of
this issue, or as a separate one? - Is there an existing testing/quality section in the docs IA this should live
under, rather than a standalone page?
Source material
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.