Command Line Interface
Reference for the Rust CLI.
Synopsis
grpctestify [OPTIONS] [TEST_PATHS]... [COMMAND]Quick workflow
- If no subcommand is provided,
grpctestifyruns tests using the provided paths runis available explicitly, but optional for normal usage- Global flags apply to commands (
-v,--completion) - Typical flow:
check->run-> report flags in CI
Precedence quick map
runmode runtime keys:section attributes > OPTIONS > CLI runtime baseline/defaultsbenchmode profile keys:CLI bench flags > BENCH section > bench defaults- Address/TLS/compression also involve env fallbacks; see OPTIONS and BENCH.
Naming migration note
- Runtime option/attribute canonical naming is snake_case (
retry_delay,no_retry,#[retry_delay],#[no_retry]).
Commands
run [TEST_PATHS]...- run tests (default command)bench [TEST_PATHS]...- run load benchmark mode for.gctfscenariosbench-compare <BASELINE> <CURRENT>- compare two bench JSON reports and gate on regressionscheck <FILES...>- validate.gctfsyntax and semantic rulesfmt <FILES...>- format.gctffilesinspect <FILE>- inspect parsed file structure (textorjson)explain <FILE>- show execution explanation (textorjson); multi-document chains also get a Mermaid sequence diagramgraph [PATHS...]- visualize directory-fixture topology (_setup/tests/_teardown) as a text tree or Mermaid flowchartlist [PATH]- list discovered tests for tooling and IDE integrationreflect [SYMBOL]- list reflected services and methods from a target servergrpcurl <FILE>- generate agrpcurlinvocation from an existing.gctffilecall <FILE>- call gRPC endpoint without assertions (or inline:call -e <pkg.Service/Method> -d '<json>', no file needed)health <ADDRESS>- check gRPC service healthlsp- start language server protocol modeindex <SOURCES...>- build/rebuild data source indexesquery [FILES...]- interactive shell or CLI query for data sourcesgen grpcurl [--execute] <grpcurl-args>- generate a.gctffile from a grpcurl invocationdocs [PATH]...- generate Markdown API docs from.gctftest files (--output,--coverage)plugins <COMMAND>- install/manage.rhaiplugins from a git hostplay- launch the web UI playground (proto reflection, saved requests, history, environments)scaffold --endpoint <SERVICE/METHOD>- generate a runnable.gctftest from a proto file, descriptor, or server reflection
Global options
-v, --verbose- verbose outputNO_COLORenv var - disable colorized output (no dedicated CLI flag)--completion <SHELL_TYPE>- install shell completion (bash,zsh,fish,elvish,powershell)
Run options
--exclude <PATTERN>- exclude files/directories by glob (repeatable)--tags <TAGS>- include only tests containing all provided tags (fromMETA.tags)--skip-tags <TAGS>- exclude tests containing any provided tags (fromMETA.tags)-p, --parallel <N|auto>- parallel workers (autoby default)-d, --dry-run- print execution plan without running requests-s, --sort <TYPE>- sort discovered test files (defaultpath)--log-format <FORMAT>- file report format (json,junit,allure,yaml,html)--log-output <OUTPUT_FILE>- output path for file report--stream- emit streaming JSON events for integration-t, --timeout <SECONDS>- per-test timeout (default30)-r, --retry <COUNT>- retry count for failed network calls (default0)--retry-delay <SECONDS>- initial retry delay (default1)--no-retry- disable retry mechanisms completely--progress <MODE>- progress mode (auto,dots,bar,none)--no-assert- skip assertion evaluation and print raw responses--coverage- generate API coverage report--coverage-format <text|json|html>- coverage output format-w, --write- write actual server responses back to test files (snapshot mode)
Note: if --log-format is set without --log-output, the run continues and file report generation is skipped with a warning.
Subcommand options
fmt:-w, --writerewrites files in place (without-w, checks formatting)check:--format <text|json>inspect:--format <text|json>explain:--format <text|json>,--against <REPORT_JSON>(post-hoc: correlate against a priorrun --log-format jsonreport — shows actual per-assertion pass/fail + timing instead of just the static plan)graph:--format <text|mermaid>list:--format <text|json>,--with-rangereflect:--address <ADDR>,--plaintext,--insecure,--format <text|json>,--list-methods,--describe <SERVICE/METHOD>,--tls-ca <FILE>,--tls-cert <FILE>,--tls-key <FILE>lsp:--stdiocall:-e <pkg.Service/Method>+-d '<json>'(inline call with no file),--insecure,--plaintext,--tls-ca <FILE>,--tls-cert <FILE>,--tls-key <FILE>(TLS flags override the file's TLS section, and are the sole TLS source in inline-emode),--bench,--concurrency <N>,--requests <N>,--duration <DURATION>health:--service <NAME>,--format <text|json>,--tls,--insecure,--timeout <SECONDS>scaffold:--endpoint <SERVICE/METHOD>,--proto <FILE_OR_DIR>,--descriptor <FILE>,--reflect,--address <ADDR>,--tls,--insecure,--plaintextbench(selected):- stop conditions:
-n, --requests,-d, --duration,--max-duration - load profile:
--max-rps,--load-schedule,--load-start,--load-step,--load-end,--load-step-duration,--load-max-duration - methodology:
--warmup,--ramp-up,--duration-stop,--skip-first,--count-errors-in-latency,--latency-percentiles - runtime/transport:
-c, --concurrency,--connections,--connect-timeout,--request-timeout,--keepalive,--cpus - validation/progress:
--assert-mode,--no-assert,--sample-rate,--progress-interval - profiles:
--profile <name>,--list-profiles,--profile-file <path>(see BENCH § Profiles) - metadata/output:
--name,--log-format(console/json/csv/ndjson/prometheus),--output,--allure-output-dir <dir>(emits the sharedallure-resultscontract — one result per benchmarked endpoint — plus a rawbenchmark-report.json)
- stop conditions:
Bench examples
# Constant profile for 60 seconds
grpctestify bench tests/ --duration 60s --concurrency 16 --max-rps 200
# Step profile (ghz-style)
grpctestify bench tests/ \
--duration 40s \
--load-schedule step \
--load-start 50 \
--load-step 10 \
--load-end 150 \
--load-step-duration 5s
# Use BENCH section defaults, override progress heartbeat
grpctestify bench tests/ --progress-interval 2sreflect --plaintext expects http://... or host:port addresses. It is rejected for explicit https://... addresses. reflect --insecure forces skip-verify even for an explicit https:// address (a bare host:port already skips verification by default).
health/scaffold --tls requests a verified TLS connection — without it, health and scaffold --reflect connect in plaintext by default (TLS-with-skip-verify only via --insecure).
Explain and graph examples
explain on a multi-document chain leads with a FLOW summary (step, endpoint, expectation kind) plus a fenced Mermaid sequence diagram — paste either straight into a markdown file and GitHub/VitePress render it natively:
grpctestify explain tests/chain.gctfsequenceDiagram
participant Client
participant Server
Client->>Server: 1. grpc.health.v1.Health/Check
Server-->>Client: response
Client->>Server: 2. grpc.health.v1.Health/Watch
Server--xClient: errorgraph visualizes directory-fixture topology (_setup.gctf → sibling tests → _teardown.gctf) across a whole directory:
grpctestify graph tests/ --format mermaidflowchart TD
subgraph s0["tests/"]
n0["chain.gctf (Check → Watch)"]
endHealth
# Check overall server health
grpctestify health localhost:50051
# Check specific service
grpctestify health localhost:50051 --service my.Service
# JSON output
grpctestify health localhost:50051 --format json
# Skip TLS verification
grpctestify health localhost:50051 --insecure
# Verified TLS connection
grpctestify health localhost:50051 --tlsCall with --bench
# Run a test file as benchmark
grpctestify call test.gctf --bench --concurrency 10 --requests 1000
# Skip TLS verification
grpctestify call test.gctf --insecureReflect
reflect's positional argument is a service symbol or service/method symbol — not a .gctf file, and not the address. Pass the server address via --address (or $GRPCTESTIFY_ADDRESS).
# List all methods with signatures
grpctestify reflect --address localhost:50051 --list-methods
# Describe a specific method
grpctestify reflect --address localhost:50051 --describe my.Service/Method
# JSON output
grpctestify reflect --address localhost:50051 --format json
# TLS client certificate
grpctestify reflect --address localhost:50051 --tls-cert client.pem --tls-key client.keyList
List discovered .gctf test files. Intended for tooling and IDE integration; the default output is JSON.
grpctestify list [PATH] [--format <text|json>] [--with-range]Flags:
PATH- file or directory to scan (optional; defaults to the current directory)--format <text|json>- output format (defaultjson)--with-range- include per-test source range information (line spans)
# List tests under a directory as JSON with source ranges
grpctestify list tests/ --with-rangeGrpcurl
Generate an equivalent grpcurl invocation from an existing .gctf file. Useful for reproducing a test call manually or in a shell script.
grpctestify grpcurl <FILE> [--doc-index <N>] [--format <text|json>]Flags:
FILE-.gctffile to convert (required)--doc-index <N>- document index for multi-document.gctffiles (1-based)--format <text|json>- output format (defaulttext)
# Print the grpcurl command for a test file
grpctestify grpcurl tests/user/get_user.gctfGen
Generate a .gctf file from an external invocation. The source is selected by a sub-subcommand; currently grpcurl is supported.
grpctestify gen [-o <OUTPUT>] grpcurl [-e|--execute] <grpcurl-args>...Flags:
-o, --output <OUTPUT>- write the generated.gctfto a file (stdout if omitted)grpcurl <grpcurl-args>...- the grpcurl arguments to translate (required; hyphen-prefixed flags are passed through verbatim)-e, --execute- run the grpcurl invocation and append the capturedRESPONSE/ERRORsection to the generated file
# Convert a grpcurl call into a .gctf file, executing it to capture the response
grpctestify gen -o get_user.gctf grpcurl -e -plaintext \
-d '{"id":"1"}' localhost:4770 user.UserService/GetUserExamples
# Run a single test
grpctestify test.gctf
# Run a directory in parallel
grpctestify tests/ --parallel 4
# Run explicit command form
grpctestify run tests/
# Create JUnit report
grpctestify tests/ --log-format junit --log-output test-results.xml
# Stream JSON events for integrations
grpctestify tests/ --stream
# Use include/exclude filtering
grpctestify tests/ --exclude "tests/legacy/**" --tags smoke --skip-tags flaky
# Validate files
grpctestify check tests/**/*.gctf
# Reflect one method signature
grpctestify reflect user.UserService/GetUser --address localhost:50051
# Format files in-place
grpctestify fmt -w .
# Check formatting (non-zero exit if changes are needed)
grpctestify fmt .
# Reflect all methods
grpctestify reflect --list-methods --address localhost:50051
# Health check
grpctestify health localhost:50051 --service my.ServiceFmt behavior
grpctestify fmt <files...>works as a formatting check and exits with code1if any file needs reformatting.grpctestify fmt -w <files...>rewrites files in place.- Safe optimizer rewrites are applied by default.
- For CI, run both
fmtandcheck.
What it changes, and what it leaves alone
Layout is the formatter's to decide. What you wrote is not.
Normalized:
- Indentation, one blank line between sections, trailing newline, CRLF to LF.
- Preamble section order (
META→BENCH→DATASET→ADDRESS→ENDPOINT→TLS→PROTO→OPTIONS); body sections keep their order. - Key order in
OPTIONSand other key-value sections (order carries no meaning there). JSON object key order is not touched. - JSON5 syntax to canonical JSON:
'single'quotes become"double", unquoted keys get quoted, trailing commas go. #comments to//inside JSON bodies.METAandDATASETare YAML, where#is the comment marker, so they are left verbatim.- Digit grouping on plain integers:
1000000becomes1_000_000. A redundant fraction shortens but never disappears:1.000000becomes1.0, never1— that would retype a float as an integer.
Left exactly as authored:
- Comments — their text and the line they sit on.
"a": 1, // notekeeps the comment on that line, and// 1000stays// 1000. - String contents.
"units": "1000000"is data, not a literal. - Numeric notation that isn't a plain integer:
0xFF,1e3,Infinity,NaN, and any digit run that is part of a larger token such as a UUID (00000000-0000-0000-0000-000000000000). - Duplicate JSON keys — both are kept, so
checkcan report them rather thanfmtsilently deleting one.
fmt is idempotent: fmt(fmt(x)) == fmt(x) for every input.