Skip to main content

Async-first Python testing framework

Project description

Apte

CI codecov docs

Write your tests and your LLM evals in one async-first framework.

An eval is just a test that returns a value - scored, not asserted. Your evals get real fixtures, dependency injection and parallelism, and live right next to the tests they ship with. Python 3.10+, installs lean (Rich UI optional).


Why Apte?

Explicit Injection (IDE-Ready)

Ctrl+Click works. Your IDE knows every type. No guessing where fixtures come from.

def test_user(db: Annotated[Database, Use(database)]): ...

Native Async & Parallelism

Tests run as coroutines on a single event loop. No plugin needed.

apte run tests:session -n 10

Smart Tagging (Tag Propagation)

Tag a fixture once, every test using it inherits the tag automatically.

@fixture(tags=["database"])
def db(): ...

session.bind(db)

@session.test()
def test_users(repo: Annotated[Repo, Use(user_repo)]): ...  # Also tagged "database"
apte run tests:session --no-tag database  # Skips ALL tests touching DB

Infra vs Code Errors (Error ≠ Fail)

✗ test_create_user: AssertionError       # Your bug - TEST FAILED
⚠ test_with_db: [FIXTURE] ConnectionError  # Infra issue - SETUP ERROR

Typed Parameterization

CODES = ForEach([200, 201])

@session.test()
def test_status(code: Annotated[int, From(CODES)]): ...

Native LLM Evals

Score model outputs alongside your tests - same fixtures, same parallelism, same apte CLI. Cases get pass/fail + numeric metrics, persisted to JSONL for run-over-run comparison.

from typing import Annotated
from apte import ForEach, From, ApteSession
from apte.evals import EvalCase, EvalSuite
from apte.evals.evaluators import contains_keywords

session = ApteSession()
chatbot_suite = EvalSuite("chatbot")
session.add_suite(chatbot_suite)

cases = ForEach([
    EvalCase(name="capital_fr", inputs="Capital of France?", expected="Paris"),
])

@chatbot_suite.eval(evaluators=[contains_keywords(keywords=["paris"])])
async def chatbot(case: Annotated[EvalCase, From(cases)]) -> str:
    return await my_agent(case.inputs)  # your LLM call
apte eval evals.session:session   # runs are recorded to .apte/history.jsonl

See Evals docs for evaluators, judges, and scoring.


Quick Start

from apte import ApteSession

session = ApteSession()


def inc(x):
    return x + 1


@session.test()
def test_answer():
    assert inc(3) == 4
apte run test_sample:session

Installation

# With uv (recommended)
uv add apte

# With pip
pip install apte

CLI

apte run module:session                    # Run tests
apte run module:session -n 4               # Parallel (4 workers)
apte run module:session --lf               # Re-run failed tests only
apte run module:session --collect-only     # List tests without running
apte run module:session --cache-clear      # Clear cache before run
apte run module:session --app-dir src      # Look for module in src/
apte run module:session --ctrf-output r.json  # CTRF report for CI/CD

apte eval module:session                   # Run LLM evals
apte eval module:session --tag safety      # Filter by case tag
apte eval module:session --last-failed     # Re-run failed cases only

Features

  • Explicit DI - No guessing which fixture you're using
  • Async native - No plugin needed, just async def
  • Parallel execution - Built-in with -n 4
  • Scoped fixtures - SESSION, SUITE, TEST
  • Mix sync/async - They just work together
  • Factory fixtures - Callables to create instances on-demand
  • Plugin system - Custom reporters, filters
  • Last-failed mode - Re-run only failed tests with --lf
  • CTRF reports - Standardized JSON for CI/CD integration
  • Native LLM evals - Scored cases, JSONL history, apte eval (see evals docs)

Why Not pytest?

pytest Apte
Fixtures Implicit (by name) Explicit (Use(fixture))
Params Hidden in fixture Visible in test (From() + factory)
Async Plugin required Native
Parallel Plugin required Built-in
Cycles Runtime error Prevented at registration
Evals External (deepeval, pydantic-…) Native (apte eval, JSONL history)

pytest has a large ecosystem and extensive community. Apte is an alternative if you prefer FastAPI-style explicit dependencies and native async in your tests.

Documentation

Full API reference, guides, and examples: renaudcepre.github.io/apte

License

MIT

Project details


Download files

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

Source Distribution

apte-0.3.1.tar.gz (110.3 kB view details)

Uploaded Source

Built Distribution

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

apte-0.3.1-py3-none-any.whl (131.9 kB view details)

Uploaded Python 3

File details

Details for the file apte-0.3.1.tar.gz.

File metadata

  • Download URL: apte-0.3.1.tar.gz
  • Upload date:
  • Size: 110.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for apte-0.3.1.tar.gz
Algorithm Hash digest
SHA256 eee2eed825b6c3f3e88a563ef75587f12eebaa31cff6c1bbf7c9396c7580a9f7
MD5 1118029410b0cdf802b0e2dfaae09e7e
BLAKE2b-256 1121e1318161f2566f0efe8b2e1d84cd7ba55feec6e179d62dcd4015d9a370ee

See more details on using hashes here.

Provenance

The following attestation bundles were made for apte-0.3.1.tar.gz:

Publisher: publish.yml on renaudcepre/apte

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file apte-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: apte-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 131.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for apte-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 788ffd425266547ed061879133cb1ac18e018fac74afb2dbeb03033ee8007f86
MD5 a5d343b270071a7eb708d5bb3a27ac54
BLAKE2b-256 0fb37b0755e783730549503679c2bad816cfe90b298d2fea411af6e3dcce50a2

See more details on using hashes here.

Provenance

The following attestation bundles were made for apte-0.3.1-py3-none-any.whl:

Publisher: publish.yml on renaudcepre/apte

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

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