@arizeai/phoenix-client/vitest ships a Vitest entrypoint plus an optional
reporter that prints a Phoenix-flavored summary at the end of the run.
Setup
Create a separatephoenix.vitest.config.ts so eval files don’t get
swept into your normal unit-test config:
includekeeps eval suites separate from unit tests by matching the*.eval.tsconvention.reporterskeeps Vitest’s default diagnostics and enables the Phoenix summary block.setupFiles: ["dotenv/config"]loadsPHOENIX_HOST,PHOENIX_API_KEY, and any other env vars from.env.testTimeoutis bumped because LLM calls can be slow.
The
jsdom test environment is not supported. Either omit environment
or set it to "node".package.json:
vitest run rather than watch mode —
many evaluators include longer-running LLM calls.
API
describe(name, fn, config?)
Declares a Phoenix test suite. The suite name is the dataset and
experiment name on the Phoenix server. describe.only and
describe.skip work like Vitest’s variants.
test(name, params, fn, timeout?)
Declares a single Phoenix test case. The params object carries the
Phoenix Example fields. test.only, test.skip, and test.each
mirror Vitest semantics. it is a re-export of test.
test.each(table)(name, fn, timeout?)
Run the same test body across many examples.
%i, %s, and %j for parity with Vitest’s
test.each. Without a placeholder the row index is appended.
Logging
px.logOutput(value)records the actualoutputfor the run.px.logAnnotation({ name, score, ... })records an annotation.px.evaluate(evaluator, params?)runs an evaluator object and records its result as an annotation linked to the evaluator trace. Evaluators can come from@arizeai/phoenix-evals.createEvaluator(),asExperimentEvaluator(), or any plain{ name, evaluate }object.
Acceptance Criteria
UseacceptanceCriteria to gate the suite on aggregate annotation scores in
CI. Criteria run after the suite finishes, so all cases still execute and the
reporter shows the full scorecard before failing. Each criterion aggregates one
annotation (by annotationName) with one metric:
metric: "average"— gate on overall quality: the mean score across all runs must clearthreshold(compared indirection).metric: "passRate"— gate on consistency: each run passes when itspassFnpredicate returnstrue, and the suite passes when the fraction of passing runs is at leastminPassRate(e.g.minPassRate: 0.9⇒ 90% must pass).
passFn receives the run’s annotation and returns a boolean, so it can express
any pass rule — a score bar, a range, a label match, a metadata check, etc.
Edge cases. An
average criterion with no numeric/boolean scores — or a
passRate criterion whose annotation was never logged — fails rather than
passing vacuously. Skipped tests are excluded from the aggregate; dry-run tests
are included because they still execute locally.
In the reporter’s Acceptance Criteria block the reported value is the mean
for average and the fraction of runs that passed for passRate (so a
fully-passing passRate criterion reads 1.000).
Reporter Output
When@arizeai/phoenix-client/vitest/reporter is loaded, the runner prints
a per-suite block at the end of the run with pass/fail counts,
annotation aggregates, acceptance criteria, and links to the Phoenix dataset and experiment.
The default Vitest reporter still runs alongside it.
Source Map
src/vitest/index.tssrc/vitest/reporter.tssrc/testing/runner.tssrc/testing/acceptance.ts

