The Skript book · Chapter 14

Testing Your Skripts

An automation is a program that runs when you are not looking. That makes testing less optional than it is for most code — and Skript's answer is to make the honest test so cheap that skipping it feels silly. Everything in this chapter is a one-liner in a terminal.

The three dials

You have met all three testing options; here they are as a method. Every skript run is fully determined by three things you control from the command line:

  • Which rule fires — --event lead.created
  • What the event carries — --data '{"budget":50}'
  • What the AI says — --score 90

Because the simulator is deterministic (same inputs, same trace, always), a command line is a test case. Save the ones that matter next to the skript, in a plain text file or a shell script, and your automation has a regression suite:

skript run triage.skript --score 90   # hot path
skript run triage.skript --score 60   # warm path
skript run triage.skript --score 10   # cold path
skript run triage.skript --score 90 --data '{"email":""}'   # hot but unreachable

Branch coverage, by hand and by eye

The discipline that pays: run every branch once before deploying. Trace ✓/✗ marks make coverage visible — if you have never seen a ✓ next to a condition, you have never tested that branch. Here is a rule with an easy-to-miss corner, and the run that catches it:

when a new lead arrives
  score it with ai
  if score is hot then
    if email is "" then
      create task "Find contact info for {name}" and stop
    reply on email "Let's talk today, {name}!"
output — real, executed at build time
▶ example.skript — Skode Skript · local simulation

  ● event: lead.created                → name: Ghost · phone: +1 555 0100 · email: 
  ⚡ score it with ai                   → score 90 · HOT
  ✓ if score is hot                    → yes
  ✓ if email is ""                     → yes
  ☑ create task "Find contact info for {name}" → created
  ■ stop                               → skript halted

  ✔ done in 2.1ms — 1 task

A hot lead with no email address: without that inner guard, the reply would have gone nowhere. The --data dial exists precisely to manufacture these awkward customers on demand.

Machine-readable runs

--json swaps the human trace for a structured report — the same facts as data:

skript run triage.skript --score 90 --json

The JSON carries the outcome of the run — score, heat, assignee, tags, every message with its channel and text, every task — which makes assertions trivial in any language or in CI. A typical CI step is exactly this:

skript check triage.skript || exit 1
skript run triage.skript --score 90 --json | grep -q '"assigned": "Maya"' || exit 1

check exits non-zero on any structural error, run --json exposes the outcome to your assertion tool of choice, and the whole thing needs only Node on the runner — no test framework, no fixtures, no mocks.

What the simulator guarantees — and what it cannot

Worth stating crisply, because good testing depends on knowing the boundary.

The simulator guarantees the language: parsing, variables, arithmetic, conditions, loops, functions, and the order and content of every action your program takes. If the trace shows assign to "Maya" after ✓ if score is hot, production will do exactly that in exactly that case.

It cannot guarantee the world: what production AI will score some particular lead, whether a phone number is a deliverable WhatsApp account, what a teammate does with a task. Those live outside any language. The craft is to keep the boundary clean — make the logic exhaustively tested where it is cheap (here), and keep the world-facing steps simple enough that no surprise can hide in them.

One more chapter of machinery — sharing skripts through the marketplace — and then we talk about production itself.