Testing
Norq includes a declarative test framework. Write assertions in tests.yaml and run them with norq test. Each test case verifies business requirements on compiled output -- subject lines contain the right text, SMS fits in one segment, Slack has action buttons, etc.
File
tests.yaml -- placed inside the notification directory alongside templates.
Test structure
tests :
- name : "Email has subject"
channel : email
sample : "New user"
assert :
subject : { contains : "Welcome" }
Each test case specifies:
Field
Required
Description
name
Yes
Human-readable test name
channel
No
Channel to test (defaults to all)
sample
No
Named sample from data.samples.yaml (defaults to first)
all_channels
No
true to run across all channels
all_samples
No
true to run across all samples
assert
Yes
Map of target -> operator -> expected value
Assertion targets
Target
Type
Description
subject
String
Email subject or push title
body
String
SMS body, Slack fallback text, email plain text
html
String
Email HTML output
sms_segments
Number
SMS segment count
payload_bytes
Number
Serialized payload size in bytes
blocks
JSON
Slack Block Kit blocks array
diagnostics
Number
Lint error/warning count
Assertion operators
String operators
assert :
subject : { eq : "Welcome, Gaurav!" }
subject : { contains : "Welcome" }
subject : { not_contains : "error" }
body : { matches : "order #[A-Z]+- \\ d+" }
Operator
Description
eq
Exact match
contains
Substring match
not_contains
Substring must not be present
matches
Regex match
Number operators
assert :
sms_segments : { lte : 1 }
payload_bytes : { lt : 4096 }
sms_segments : { gte : 1 }
Operator
Description
eq
Equal
lt
Less than
lte
Less than or equal
gt
Greater than
gte
Greater than or equal
Block operators (Slack)
assert :
blocks : { any : { type : "header" } }
blocks : { any : { type : "actions" } }
Operator
Description
any
At least one block matches the partial JSON
Diagnostic operators
assert :
diagnostics : { errors : 0 }
Operator
Description
errors
Exact count of lint errors
Examples
Smoke test (all channels render)
tests :
- name : "All render without errors"
all_channels : true
all_samples : true
assert :
diagnostics : { errors : 0 }
SMS segment budget
tests :
- name : "SMS fits 1 segment"
channel : sms
sample : "New user"
assert :
sms_segments : { lte : 1 }
- name : "SMS fits 2 segments with long data"
channel : sms
sample : "Pro user with long name"
assert :
sms_segments : { lte : 2 }
Email content checks
tests :
- name : "Email subject has order ID"
channel : email
sample : "Shipped with discount"
assert :
subject : { contains : "ORD-" }
- name : "Email has unsubscribe link"
channel : email
sample : "New user"
assert :
html : { contains : "unsubscribe" }
Slack structure
tests :
- name : "Slack has primary action"
channel : slack
sample : "New user"
assert :
blocks : { any : { type : "actions" } }
- name : "Slack has header"
channel : slack
sample : "New user"
assert :
blocks : { any : { type : "header" } }
Push payload size
tests :
- name : "Push under 4KB"
channel : push
sample : "New user"
assert :
payload_bytes : { lte : 4096 }
Running tests
# Test all notifications
norq test
# Test one notification
norq test order-shipped
# JSON output (for CI)
norq test --json
CI integration
Add to your CI pipeline:
# GitHub Actions
- run : norq lint --json
- run : norq test --json
Lint checks structural correctness (valid schemas, no duplicate channels, etc.). Tests check business requirements (content, size limits, structure).
Relationship to lint
Check type
Tool
Purpose
Schema valid
norq lint
Data contract is valid JSON Schema
Templates parse
norq lint
No syntax errors
Null safety
norq lint
Nullable fields guarded with :::if
Subject has text
norq test
Business assertion
SMS fits budget
norq test
Business assertion
Slack has buttons
norq test
Business assertion
Use both in CI. Lint catches structural issues; tests catch content/business issues.