Skip to main content

skilltest-pytest

A pytest plugin for skilltest: run AI-skill tests and natural-language evals as ordinary pytest tests, and mix in your own deterministic checks. Built on skilltest-sdk — the SDK's code API is re-exported here, so a pytest suite needs only this one dependency.

Define the whole case in code (recommended)

Build the case — skill, input, evals, an optional simulated user, mocks — right in the test. Everything the YAML carries has a typed builder, so the case, its mocks, and any deterministic transcript checks live in one place:

from skilltest_pytest import TestCase, run_skill, boolean, numeric, describe_failures

def test_greeter():
    case = TestCase(
        skill="skills/greeter",           # resolved relative to the working dir
        input="Greet Dr. Smith, who has an appointment today.",
        evals=[
            boolean("the reply greets Dr. Smith by name"),
            numeric("how warm is the tone", min=0, max=10, threshold=7),
        ],
    )
    report = run_skill(case, platforms=["claude-code"], models=["claude-opus-4-8"])
    assert report.passed, describe_failures(report)

Multi-turn cases add user(...); deterministic call-count checks use called / not_called taking the stub/spy object itself or a name you gave it (or use the mock objects' own assertions — see below). run_skill also takes platforms=/models= to fan a case across a matrix.

Or point at a YAML file

run_skill accepts a path just as well (run_skill("cases/greet.yaml")), and auto-collection still works: name a case something.skilltest.yaml and pytest runs it with no test function at all —

# greet.skilltest.yaml
skill: ./skills/greeter
input: "Greet Dr. Smith."
evals:
  - type: boolean
    criterion: "the reply greets Dr. Smith by name"

The full field reference for both forms is docs/schema.md.

Assert on tool use, and stream

The SDK's tool-event and streaming surfaces are re-exported too. tool_calls returns the normalized tool_call events a run took (each a ToolEvent with kind/name/input/output/index), and stream_skill yields them live so a test can short-circuit on bad behavior:

from skilltest_pytest import TestCase, run_skill, tool_calls, boolean

EDIT_CASE = TestCase(
    skill="skills/editor",
    input="Update the config and commit it.",
    evals=[boolean("the change was committed")],
)

def test_commits_but_never_deletes():
    report = run_skill(EDIT_CASE)
    calls = tool_calls(report.runs[0].transcript)
    assert any("git commit" in str(c.input) for c in calls)
    assert not any("rm -rf" in str(c.input) for c in calls)
import asyncio
from skilltest_pytest import stream_skill

def test_makes_no_network_call():
    async def go():
        async for ev in stream_skill(EDIT_CASE):
            assert ev.event.name != "curl", "skill made a network call"
    asyncio.run(go())   # or use pytest-asyncio and `async def test_...`

Configuration

The plugin shells out to the skilltest binary. Point it at one with the SKILLTEST_BIN env var (or bin=), the provider with SKILLTEST_PROVIDER (or provider=), and set defaults in pyproject.toml:

[tool.pytest.ini_options]
skilltest_provider = "oneharness"
skilltest_platforms = ["claude-code"]
skilltest_models = ["claude-opus-4-8"]

See the repository root for the provider protocol and the full schema.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

skilltest_pytest-0.9.0.tar.gz (22.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

skilltest_pytest-0.9.0-py3-none-any.whl (5.5 kB view details)

Uploaded Python 3

File details

Details for the file skilltest_pytest-0.9.0.tar.gz.

File metadata

  • Download URL: skilltest_pytest-0.9.0.tar.gz
  • Upload date:
  • Size: 22.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for skilltest_pytest-0.9.0.tar.gz
Algorithm Hash digest
SHA256 731189c3f6f06461c14702841bde1fe9f0d8a4e6644b24773d59f778f8a76eb3
MD5 d40b187cf3f5438a3ba1713523bb3681
BLAKE2b-256 2de02b2910fdf4714a642353561cc4120cc583bff318fa5f3b98de0a79022765

See more details on using hashes here.

File details

Details for the file skilltest_pytest-0.9.0-py3-none-any.whl.

File metadata

  • Download URL: skilltest_pytest-0.9.0-py3-none-any.whl
  • Upload date:
  • Size: 5.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.27 {"installer":{"name":"uv","version":"0.11.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for skilltest_pytest-0.9.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4514daa74cdb3ed5c054f20932dc90a93ac85b11f6efe8a5b4799c4a3651bb2e
MD5 6a48bd0c8ab681c1b48c00595f60c90a
BLAKE2b-256 6fae6fb44c7ee3ebb6dd0e553f43594b474ff83a0fdba1827304636260ee9cd1

See more details on using hashes here.

Release history Release notifications | RSS feed

0.10.0

2 files

This release

0.9.0 This release

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page