Rendezvous Reference
Complete reference for Rendezvous CLI commands, configuration options, and advanced usage.
This reference explains how to use Rendezvous in different situations. By the end, you'll know when and how to use its features effectively.
What's Inside
Running Rendezvous
To run Rendezvous, use the following command:
Let's break down each part of the command.
Positional Arguments
Consider this example Clarinet project structure:
1. Path to the Clarinet Project
The <path-to-clarinet-project> is the relative or absolute path to the root directory of the Clarinet project. This is where the Clarinet.toml file exists. It is not the path to the Clarinet.toml file itself.
For example, if you're in the parent directory of root, the correct relative path would be:
2. Contract Name
The <contract-name> is the name of the contract to be tested, as defined in Clarinet.toml.
For example, if Clarinet.toml contains:
To test the contract named contract, you would run:
3. Testing Type
The <type> argument specifies the testing technique to use. The available options are:
test– Runs property-based tests.invariant– Runs invariant tests.
For a deeper understanding of these techniques and when to use each, see the Testing Methodologies chapter of the Rendezvous Docs.
Running property-based tests
Property-based testing requires one or more test functions (e.g. test-xyz) in the contract file (contract.clar), annotated with ;; #[env(simnet)].
Then, execute:
This tells Rendezvous to:
Load the Clarinet project located in
./root.Target the contract named
contractas defined inClarinet.tomlby executing property-based tests defined within the contract.
Running invariant tests
Invariant testing requires two things in the contract file (contract.clar), both annotated with ;; #[env(simnet)]:
One or more invariant functions (e.g.
invariant-xyz).The Rendezvous context — the
contextmap andupdate-contextfunction (see The Rendezvous Context).
To run invariant tests, use:
With this command, Rendezvous will:
Randomly execute public function calls in the
contractcontract.Randomly check the defined invariants to ensure the contract's internal state remains valid.
If an invariant check fails, it means the contract's state has deviated from expected behavior, revealing potential bugs.
Options
Rendezvous also provides additional options to customize test execution:
1. Customizing the Number of Runs
By default, Rendezvous runs 100 test iterations. You can modify this using the --runs option:
This increases the number of test cases to 500.
2. Replaying a Specific Sequence of Events
To reproduce a previous test sequence, you can use the --seed option. This ensures that the same random values are used across test runs:
How to Find the Replay Seed
When Rendezvous detects an issue, it includes the seed needed to reproduce the test in the failure report. Here’s an example of a failure report with the seed:
In this case, the seed is 426141810. You can use it to rerun the exact same test scenario:
3. Stop After First Failure
By default, Rendezvous will start the shrinking process after finding a failure. To stop immediately when the first failure is detected, use the --bail option:
This is useful when you want to examine the first failure without waiting for the complete test run and shrinking process to finish.
4. Using Dialers
Dialers allow you to define pre- and post-execution functions using JavaScript during invariant testing. To use a custom dialer file, run:
A good example of a dialer can be found in the Rendezvous repository, within the example Clarinet project, inside the sip010.cjs file.
In that file, you’ll find a post-dialer designed as a sanity check for SIP-010 token contracts. It ensures that the transfer function correctly emits the required print event containing the memo, as specified in SIP-010.
How Dialers Work
During invariant testing, Rendezvous picks up dialers when executing public function calls:
Pre-dialers run before each public function call.
Post-dialers run after each public function call.
Both have access to an object containing:
selectedFunction– The function being executed.functionCall– The result of the function call (undefinedfor pre-dialers).clarityValueArguments– The generated Clarity values used as arguments.
Example: Post-Dialer for SIP-010
Below is a post-dialer that verifies SIP-010 compliance by ensuring that the transfer function emits a print event containing the memo.
This dialer ensures that any SIP-010 token contract properly emits the memo print event during transfers, helping to catch deviations from the standard.
5. Regression Testing
Rendezvous automatically saves failing test cases to prevent regressions. When a test fails, its seed and configuration are persisted to disk. On subsequent runs, you can replay these failures to ensure bugs stay fixed.
By default, Rendezvous runs fresh random tests:
To verify that previously discovered bugs remain fixed, use the --regr flag:
Rendezvous loads all saved failures for the contract and replays them using their original seeds.
How Failure Persistence Works
When Rendezvous detects a failure, it automatically saves the test configuration to:
For example, failures in the counter contract deployed by ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM would be saved to:
The regression file stores failures grouped by test type (test vs invariant). Each failure record includes:
seed– The random seed that triggered the failure.numRuns– Number of test iterations needed for the failure to occur.timestamp– When the failure was discovered (Unix timestamp in milliseconds).dial(optional) – Path to the dialer file used during the test.
Failures are sorted by timestamp in descending order, with the most recent first. To clear saved regressions for a contract, delete its regression file (or edit it to remove specific failures while keeping others).
6. Using a Config File
Instead of passing options as CLI flags, you can provide a JSON config file with --config. When a config file is used, all run options come from the file exclusively — CLI flags like --seed or --runs are ignored.
A config file is a JSON object with optional fields:
accounts
array of objects
Custom accounts (name and address fields required).
accounts_mode
string
"overwrite" (default) or "concatenate".
seed
integer
Seed for replay functionality.
runs
positive integer
Number of test iterations.
bail
boolean
Stop on first failure.
regr
boolean
Run regression tests only.
dial
string
Path to custom dialers file.
The accounts field lets you define custom accounts (for example, mainnet "whale" addresses) for testing. By default ("overwrite" mode), these replace the Devnet.toml accounts entirely. With "concatenate" mode, config accounts are merged with the existing Devnet accounts — if a name appears in both, the config account's address takes precedence.
Rendezvous warns if the config file contains unrecognized keys (e.g. a typo like "sedd" instead of "seed"), and also warns if CLI flags are passed alongside --config.
Summary
<path-to-clarinet-project>
Path to the Clarinet project (where Clarinet.toml is located).
rv root contract test
<contract-name>
Name of the contract to test (as in Clarinet.toml).
rv root contract test
<type>
Type of test (test for property-based tests, invariant for invariant tests).
rv root contract test
--runs=<num>
Sets the number of test iterations (default: 100).
rv root contract test --runs=500
--seed=<num>
Uses a specific seed for reproducibility.
rv root contract test --seed=12345
--dial=<file>
Loads JavaScript dialers from a file for pre/post-processing.
rv root contract test --dial=./custom-dialer.js
--regr
Run regression tests only (replay saved failures).
rv root contract test --regr
--bail
Stop after the first failure.
rv root contract test --bail
--config=<file>
Uses a JSON config file for all run options.
rv root contract test --config=rv.config.json
Understanding Rendezvous
Rendezvous makes property-based tests and invariant tests first-class. Tests are written in the same language as the system under test. This helps developers master the contract language. It also pushes boundaries—programmers shape their thoughts first, then express them using the language's tools.
When Rendezvous initializes a Simnet session using a given Clarinet project, it deploys the contracts as defined in Clarinet.toml. The contract source, its test functions, and the context all live in the same file — the test code is marked simnet-only with the ;; #[env(simnet)] annotation, so it is included during Simnet testing but stripped on deployment to real networks.
Example
Let’s say we have a contract named checker. The contract source, test functions, and context all live in the same checker.clar file:
The ;; #[env(simnet)] annotation ensures the test functions and context are only deployed during Simnet testing. While the contract source and test functions are familiar, the context is new. Let's take a closer look at it.
The Rendezvous Context
Rendezvous uses a context to track function calls and execution details during invariant testing. This allows for better tracking of execution details and invariant validation.
Every contract tested with Rendezvous invariant testing must include the context map and the update-context private function (both annotated with ;; #[env(simnet)]). During invariant testing, Rendezvous calls public functions and uses update-context to track successful executions, which lets invariants reason about how many times each function has been called. If these are missing during invariant testing, Rendezvous throws a runtime error. The context is not required for property-based testing.
How the Context Works
When a function is successfully executed during a test, Rendezvous records its execution details in a Clarity map. This map helps track how often specific functions are called successfully and can be extended for additional tracking in the future.
Here’s how the context is structured:
Breaking it down
contextmap → Keeps track of execution data, storing how many times each function has been called successfully.update-contextfunction → Updates thecontextmap whenever a function executes, ensuring accurate tracking.
Using the context to write invariants
By tracking function calls, the context helps invariants ensure stronger correctness guarantees. For example, an invariant can verify that a counter stays above zero by checking the number of successful increment and decrement calls.
Example invariant using the context
By embedding execution tracking into the contract, Rendezvous enables more effective smart contract testing, making it easier to catch bugs and check the contract correctness.
Discarding Property-Based Tests
Rendezvous generates a wide range of inputs, but not all inputs are valid for every test. To skip tests with invalid inputs, there are two approaches:
Discard Function
A separate function determines whether a test should run.
Rules for a Discard Function:
Must be read-only.
Name must match the property test function, prefixed with
"can-".Parameters must mirror the property test’s parameters.
Must return
trueonly if inputs are valid, allowing the test to run.
Discard function example
Here, can-test-add ensures that the test never executes for n <= 1.
In-Place Discarding
Instead of using a separate function, the test itself decides whether to run. If the inputs are invalid, the test returns (ok false), discarding itself.
In-place discarding example
In this case, if n <= 1, the test discards itself by returning (ok false), skipping execution.
Discarding summary
Discard Mechanism
When to Use
Discard Function
When skipping execution before running the test is necessary.
In-Place Discarding
When discarding logic is simple and part of the test itself.
In general, in-place discarding is preferred because it keeps test logic together and is easier to maintain. Use a discard function only when it's important to prevent execution entirely.
Custom Manifest Files
Some smart contracts need a special Clarinet.toml file to allow Rendezvous to create state transitions in the contract. Rendezvous supports this feature by automatically searching for Clarinet-<target-contract-name>.toml first. This allows you to use test doubles while keeping tests easy to manage.
Why use a custom manifest?
A great example is the sBTC contract suite.
For testing the sbtc-token contract, the sbtc-registry authorization function is-protocol-caller is too restrictive. Normally, it only allows calls from protocol contracts, making it impossible to directly test certain state transitions in sbtc-token.
To work around this, you need two things:
A test double for sbtc-registry
You can create an sbtc-registry test double called sbtc-registry-double.clar:
This loosens the restriction just enough for testing by allowing the deployer to act as a protocol caller, while still enforcing an access check.
A Custom Manifest File
Next, create Clarinet-sbtc-token.toml to tell Rendezvous to use the test double only when targeting sbtc-token:
How It Works
When testing
sbtc-token, Rendezvous first checks ifClarinet-sbtc-token.tomlexists.If found, it uses this file to initialize Simnet.
If not, it falls back to the standard
Clarinet.toml.
This ensures that the test double is only used when testing sbtc-token, keeping tests realistic while allowing necessary state transitions.
Trait Reference Parameters
Rendezvous automatically generates arguments for function calls. It handles most Clarity types without any setup from you. However, trait references require special handling since Rendezvous cannot generate them automatically.
How Trait Reference Selection Works
When your functions accept trait reference parameters, you must include at least one trait implementation in your Clarinet project. This can be either a project contract or a requirement.
Here's how Rendezvous handles trait references:
Project Scanning – Before testing begins, Rendezvous scans your project for functions that use trait references.
Implementation Discovery – It searches the contract AST for matching trait implementations and adds them to a selection pool.
Random Selection – During test execution, Rendezvous randomly picks an implementation from the pool and uses it as a function argument.
This process allows Rendezvous to create meaningful state transitions and validate your invariants or property-based tests.
Example
The example Clarinet project demonstrates this feature. The send-tokens contract contains one public function and one property-based test that both accept trait references.
To enable testing, the project includes rendezvous-token, which implements the required trait.
Adding More Implementations
You can include multiple eligible trait implementations in your project. Adding more implementations allows Rendezvous to introduce greater randomness during testing and increases behavioral diversity. If a function that accepts a trait implementation parameter is called X times, those calls are distributed across the available implementations. As the number of implementations grows, Rendezvous has more options to choose from on each call, producing a wider range of behaviors — and uncovering edge cases that may be missed when relying on a single implementation.
Library API
Beyond the rv CLI, Rendezvous can be used as a TypeScript library for building custom property-based testing strategies. You import its argument-generation capabilities directly and compose your own fast-check properties — useful when you need full control over the testing loop (custom assertions, stateful setups, multi-contract interactions, or integration with Vitest/Jest).
Rendezvous ships with TypeScript declarations. It relies on fast-check and @stacks/clarinet-sdk (both already dependencies).
getContractFunction
getContractFunction(simnet, contractName, functionName, deployer?)
Retrieves a function interface from a deployed contract, enriched with trait-reference data when applicable. Throws if the contract or function is not found.
simnet
Simnet
The simnet instance from initSimnet.
contractName
string
The contract name (e.g., "counter").
functionName
string
The function name (e.g., "increment").
deployer
string
Optional. Deployer address. Defaults to simnet.deployer.
Returns: EnrichedContractInterfaceFunction
strategyFor
strategyFor(simnet, fn, allAddresses?, projectTraitImplementations?)
Returns an fc.Arbitrary<ClarityValue[]> ready for use with simnet.callPublicFn, simnet.callReadOnlyFn, or simnet.callPrivateFn. It handles all Clarity types automatically: uint, int, bool, principal, buff, string-ascii, string-utf8, list, tuple, optional, response, and trait_reference (including recursive and nested structures such as a list of tuples or an optional of a response).
simnet
Simnet
The simnet instance.
fn
EnrichedContractInterfaceFunction
Function interface from getContractFunction.
allAddresses
string[]
Optional. Addresses for principal-typed arguments. Defaults to every account in the simnet.
projectTraitImplementations
Record<string, ImplementedTraitType[]>
Optional. Trait implementations keyed by trait. Defaults to extracting them from the simnet.
Returns: fc.Arbitrary<ClarityValue[]>
Example
For the full Library API reference — including custom deployers, restricting the principal pool, and the complete supported-type table — see the Library API chapter of the Rendezvous Book.
Last updated
Was this helpful?