Every Varianz test does one or both of two things to a VPoint: override it (control what it returns) or observe it (capture what actually happened, without changing it). Both are single CEL expressions inserted into your test session.
This guide assumes the test lifecycle from How tests work: insert → sync → trigger → assert.
Overrides
An expression without invoke() fully replaces the function’s result. The real code never runs:
All required fields of a struct must be provided, names are case-sensitive, and the type name is the schema’s simple name (ChargeResult, not a package-qualified one). If you’re unsure of the exact names, inspect the schema first — get_vpoint_detail shows types, fields, and examples.
Conditional overrides
args.<param> reads the call’s inputs, and invoke() executes the real function — combine them to intercept only the calls you care about:
invoke() returns the real result, so you can post-process it:
ctx.<fn>(...) calls a context function the service registered.
Observing with samples
sample("name", value) captures a value for your test and returns it unchanged. Wrapping invoke() gives you a probe — the real function runs, and you get a copy of its result:
Samples stream back to the test session, where you assert with the fluent API (identical shape in every test SDK):
You can sample anything, not just the result — sample("input-amount", args.input.amount) captures an argument, and samples compose with overrides:
The simple composition to remember: sample("x", invoke()) observes; a bare struct literal overrides; condition ? override : invoke() does both selectively.
Negative assertions
To verify something did not happen — the notification that must not fire after a declined payment — probe it and assert the sample never arrives:
Keep the negative-window timeout short — it bounds how long the test waits to prove absence.
A complete example
Overriding one service while observing another is the bread-and-butter multi-service test:
The same test drives services written in any supported language — see Test across services.
Rules worth memorizing
- Inputs are always
args.<param>. Bare names and $. roots are rejected when the stage compiles.
invoke() takes zero arguments (forward the original args) or exactly the VPoint’s arity (substitute different ones).
sample(name, value) takes exactly two arguments and returns value.
- Always
await_sync_or_fail between insert and trigger — without it, the stage may not have reached the service yet.
- Full syntax, optionals, list predicates, and current limitations: CEL reference.