Skip to content

Python API

CoverageMap

CoverageMap

Tracks API test coverage against an OpenAPI specification.

Note

Examples on this page that use coverage without creating it assume a map built with coverage = tracecov.CoverageMap.from_path("openapi.json").

from_dict

from_dict(schema, *, base_path=None, location=None)

Create a coverage map from a parsed OpenAPI specification.

Parameters:

Name Type Description Default
schema dict Parsed OpenAPI specification required
base_path str | None Base path prefix for all operations. When omitted, taken from the path of servers[0].url (OpenAPI 3) or basePath (Swagger 2.0) None
location str | None File path or URL of the specification, shown in error messages. $refs to other files resolve against its directory, or against the working directory when it is None; a URL reads no other files None

Example

import tracecov

# From a dictionary, for example loaded from a file or an API response
schema = {"openapi": "3.0.0", "info": {"title": "API", "version": "1.0"}, "paths": {}}
coverage = tracecov.CoverageMap.from_dict(schema)

# With base path for APIs hosted under a prefix
coverage = tracecov.CoverageMap.from_dict(schema, base_path="/api/v1")

Tip

To load a schema from a URL, use from_url() instead.

from_path

from_path(path, *, base_path=None)

Create a coverage map from an OpenAPI specification file.

Parameters:

Name Type Description Default
path str | Path Path to OpenAPI specification (JSON or YAML) required
base_path str | None Base path prefix for all operations. When omitted, taken from the path of servers[0].url (OpenAPI 3) or basePath (Swagger 2.0) None

Example

import tracecov

coverage = tracecov.CoverageMap.from_path("openapi.json")
coverage = tracecov.CoverageMap.from_path("openapi.yaml", base_path="/api/v1")

from_url

from_url(url, *, base_path=None)

Create a coverage map from an OpenAPI specification URL.

Fetches the schema from the given URL and parses it as JSON or YAML.

Parameters:

Name Type Description Default
url str URL to fetch the OpenAPI specification from required
base_path str | None Base path prefix for all operations. When omitted, taken from the path of servers[0].url (OpenAPI 3) or basePath (Swagger 2.0) None

Example

import tracecov

coverage = tracecov.CoverageMap.from_url("https://api.example.com/openapi.json")
coverage = tracecov.CoverageMap.from_url(
    "https://api.example.com/openapi.yaml",
    base_path="/api/v1"
)

record

record(request, response=None, timestamp=None, labels=None)

Record an HTTP interaction for coverage tracking.

Parameters:

Name Type Description Default
request HttpRequest Request to record required
response HttpResponse | None Response to record None
timestamp float | None Unix timestamp of the interaction None
labels Iterable[str] | None Coverage labels for this interaction, added to those of the enclosing labeled() blocks. Professional only None

Example

from tracecov import HttpRequest, HttpResponse

coverage.record(
    request=HttpRequest(
        method="GET",
        url="https://api.example.com/users/1",
        headers={"Authorization": "Bearer token"},
    ),
    response=HttpResponse(
        status_code=200,
        elapsed=0.5,
        body=b'{"id": 1, "name": "Alice"}',
        headers={"Content-Type": "application/json"},
    ),
)

record_error

record_error(method, path, message)

Attach an error message to an operation. It appears in the operation's errors list in reports.

Parameters:

Name Type Description Default
method str HTTP method required
path str Path template as written in the specification required
message str Error message required

generate_report

generate_report(*, format="html", title=None)

Generate a coverage report.

Parameters:

Name Type Description Default
format str Output format: "html", "json", or "markdown" "html"
title str | None Custom title for HTML reports None

Returns: Report content as a string. See JSON Report Format for JSON schema and usage. Every percent in the JSON report is null when that dimension's total is 0.

Example

import json

# HTML report
html = coverage.generate_report(title="My API Coverage")

# JSON report for CI integration
data = json.loads(coverage.generate_report(format="json"))
percent = data["summary"]["operations"]["percent"]
if percent is not None and percent < 80:
    raise SystemExit("Coverage below threshold")

save_report

save_report(*, output_file=None, format="html", title=None)

Save a coverage report to a file.

Parameters:

Name Type Description Default
output_file str | None Output path (coverage.html, coverage.json, or coverage.md by default) None
format str Output format: "html", "json", or "markdown" "html"
title str | None Custom title for HTML reports None

Example

coverage.save_report()  # saves to coverage.html
coverage.save_report(output_file="report.html", title="API Coverage")
coverage.save_report(format="json")  # saves to coverage.json

generate_html_report / generate_json_report

generate_html_report(*, title=None)

generate_json_report()

Same as generate_report(format="html", title=...) and generate_report(format="json").

Returns: Report content as a string.

generate_markdown_report

generate_markdown_report(*, report_url=None, weak_threshold=None, is_pull_request=False, sub_text=None, columns=None)

Generate a Markdown report for GitHub step summaries and pull request comments.

Parameters:

Name Type Description Default
report_url str | None Link to the full HTML report, shown in the footer None
weak_threshold float | None Parameter coverage ratio (0.0–1.0) below which an operation is flagged 1.0
is_pull_request bool Word the headline as the effect of merging a pull request False
sub_text str | None Text shown in <sub> under the footer None
columns list[str] | None Columns to show: "parameters", "keywords", "responses", "examples". Must not be empty all

Returns: Markdown report as a string.

Example

markdown = coverage.generate_markdown_report(
    report_url="https://ci.example.com/coverage.html",
    is_pull_request=True,
    columns=["parameters", "responses"],
)

save_html_report / save_json_report / save_markdown_report

save_html_report(*, output_file=None, title=None)

save_json_report(*, output_file=None)

save_markdown_report(*, output_file=None, report_url=None, weak_threshold=None, is_pull_request=False, sub_text=None, columns=None)

Write the corresponding report to output_file: coverage.html, coverage.json, or coverage.md by default. The remaining parameters match generate_html_report() and generate_markdown_report().

build_json_report

build_json_report()

The JSON report as a dict, without serializing it to a string.

Returns: dict

operation_report / operation_reports

operation_report(method, path)

operation_reports()

Per-operation entries of the JSON report. path is the path as it appears in the report, including the base path. operation_report() returns None when no such operation exists.

Returns: dict | None and list[dict].

Example

report = coverage.operation_report("GET", "/users/{id}")
if report is not None:
    print(report["coverage"]["parameters"])

generate_text_report

generate_text_report(*, width, colored=True, skip_covered=False, skip_empty=False, show_missing=None)

Generate a text coverage report for terminal output.

Parameters:

Name Type Description Default
width int Terminal width for formatting required
colored bool Use ANSI colors in output True
skip_covered bool Hide fully covered items False
skip_empty bool Hide items with no traffic False
show_missing list[str] | None Sections listing uncovered items. The only accepted value is "parameter" None

Returns: Formatted text report as a string.

Example

print(coverage.generate_text_report(width=80, colored=False, show_missing=["parameter"]))

# Compact view without fully covered items
report = coverage.generate_text_report(width=80, skip_covered=True)

Output after recording a single GET /users/1:

Operation                                      Params   Missed   Partial   Cover
--------------------------------------------------------------------------------
GET /users                                          1        1         0   0.00%
POST /users                                         1        1         0   0.00%
GET /users/{id}                                     1        0         1   0.00%
--------------------------------------------------------------------------------
TOTAL                                               3        2         1   0.00%

Uncovered Parameters:
GET /users:
    query: limit
POST /users:
    body: application/json

statistic

statistic()

Get aggregate coverage statistics.

Returns: Dictionary with keys:

  • operations - {"seen": int, "total": int, "timeline": list}
  • parameters - {"positive": int, "negative": int, "partial": int, "full": int, "total": int, "timeline": list}
  • keywords - {"positive": int, "negative": int, "partial": int, "full": int, "total": int, "timeline": list}
  • examples - {"seen": int, "total": int, "timeline": list}
  • responses - {"seen": int, "total": int, "elapsed_total_seconds": float, "timeline": list}
  • response_keywords - {"reached": int, "violated": int, "total": int}

Example

stats = coverage.statistic()
print(f"Operations: {stats['operations']['seen']}/{stats['operations']['total']}")
print(f"Keywords: {stats['keywords']['full']}/{stats['keywords']['total']}")

check_thresholds

check_thresholds(*, operations=None, parameters=None, keywords=None, examples=None, responses=None, response_keywords=None, label=None)

Compare coverage against minimum percentages (0–100). Unset dimensions and dimensions with a total of 0 are skipped. With label, compare that label's coverage instead of the totals.

Raises: ValueError when label names a label with no recorded traffic.

Returns: List of violations, empty when every threshold is met. Each is a dictionary with keys:

  • dimension - "operations", "parameters", "keywords", "examples", "responses", or "response_keywords"
  • actual - Measured percentage
  • minimum - Required percentage

Example

violations = coverage.check_thresholds(operations=80, parameters=80)
for v in violations:
    print(f"{v['dimension']}: {v['actual']:.1f}% < {v['minimum']:.0f}%")

coverage_gaps

coverage_gaps()

Get everything the tests have not covered yet, as a flat list.

Returns: List of dictionaries. Each has method, path, and kind; the other keys depend on kind:

kind Extra keys
"operation_unseen" -
"response_uncovered" status_code
"parameter_uncovered" location, parameter
"keyword_miss", "keyword_needs_valid", "keyword_needs_invalid" location, parameter, schema_path, satisfiability
"example_unseen" location, parameter

location is one of "path", "query", "querystring", "header", "cookie", "body", "form_data". satisfiability is "Normal", "Positive", or "Negative".

Example

for gap in coverage.coverage_gaps():
    if gap["kind"] == "keyword_needs_invalid":
        print(f"{gap['method']} {gap['path']} {gap['parameter']}{gap['schema_path']}: needs an invalid value")

uncovered_keywords

uncovered_keywords()

Deprecated

Use coverage_gaps() instead.

Get a list of keywords without full coverage.

Returns: List of dictionaries with keys:

  • method - HTTP method
  • path - API path
  • location - Parameter location (path, query, querystring, header, cookie, body, form_data)
  • parameter - Parameter name
  • schema_path - JSON pointer to the keyword in the schema
  • state - What coverage is missing: "miss" (no values at all), "needs_invalid", or "needs_valid"
  • satisfiability - Satisfiability constraint ("Normal", "Positive", or "Negative")

suggestions

suggestions()

Requests that would cover the keywords your tests missed, one entry per operation and parameter location. Professional only; Community raises ValueError.

Returns: List of dictionaries with keys:

  • method, path - The operation
  • location - "path", "query", "header", "cookie", "form_data", or "body"
  • media_type - Body only
  • gaps - Uncovered keywords, each with parameter, pointer (the keyword within the parameter's schema) and polarity ("valid" or "invalid")
  • requests - Suggested requests, each with polarity, value (parameters by name, or the body itself) and covers (indexes into gaps)
  • hidden - Suggested requests left out of requests, which keeps the widest ones
  • uncovered - Gaps no request covers, each with gap (an index into gaps) and reason: "unsupported" (not targeted yet), "unsatisfiable" (no value reaches it), "exhausted" (no attempt covered it) or "heavy" (the time budget ran out first)

Example

After a single GET /items?limit=10 against limit: {"type": "integer", "minimum": 1, "maximum": 100}:

for entry in coverage.suggestions():
    for request in entry["requests"]:
        print(entry["method"], entry["path"], request["polarity"], request["value"])
GET /items invalid {'limit': 101}
GET /items invalid {'limit': 0}
GET /items invalid {'limit': None}

diagnostics

diagnostics()

Problems found in the specification that affect coverage, such as invalid patterns or unresolvable references.

Returns: List of dictionaries with severity ("error" or "warning"), kind, at (where the problem is), and the fields that kind carries. See Diagnostics for the kinds.

Example

for diagnostic in coverage.diagnostics():
    print(diagnostic["severity"], diagnostic["kind"], diagnostic["at"])

A query parameter whose pattern is "[a-" produces:

{
    "severity": "warning",
    "kind": "invalid_schema",
    "schema_path": "/pattern",
    "message": '`pattern` must be a valid regex; found `"[a-"`',
    "at": {"path": "/users", "in": "parameter", "method": "GET", "location": "query", "name": "q"},
}

unmatched_requests

unmatched_requests()

Get the requests that matched no documented operation, most frequent first. Grouped by (method, host, path). Reporting only - never affects coverage percentages or thresholds.

Returns: List of dictionaries with keys:

  • method - HTTP method
  • host - Request authority, including the port when present
  • path - Path template for method_not_documented, observed request path otherwise
  • reason - "no_matching_path" or "method_not_documented"
  • count - Requests in this group
  • first_seen_at - Timestamp of the first request recorded into the group

At most 1000 distinct groups are tracked; see unmatched_overflow().

Example

for entry in coverage.unmatched_requests():
    print(f"{entry['method']} {entry['host']}{entry['path']}: {entry['reason']} (x{entry['count']})")

unmatched_overflow

unmatched_overflow()

Requests dropped after the distinct-group cap was reached. A non-zero value means unmatched_requests() is truncated.

Returns: int

dropped_requests

dropped_requests()

Requests discarded before matching could be attempted - an unparsable URL (an unresolved {{baseUrl}} in a Postman collection, say) or an HTTP method TraceCov does not model. A non-zero value with empty unmatched_requests() explains zero coverage.

Returns: int

export_counts

export_counts()

Everything this map recorded, as opaque bytes for merge_counts() on a map of the same specification in another process. The pytest plugin uses it to collect coverage from pytest-xdist workers.

Returns: bytes

merge_counts

merge_counts(counts)

Add counts that export_counts() returned for a map of the same specification, as if this map had recorded their traffic too.

Parameters:

Name Type Description Default
counts bytes What export_counts() returned required

Raises: ValueError when counts is malformed or does not fit this map's specification. Counts from another specification of the same shape are not detected.

covered_by

covered_by(method, path, *, location=None, name=None, keyword=None, status=None, media_type=None)

Get the coverage labels that reached an element, in name order. Professional only.

  • Operation: method and path alone.
  • Parameter: location ("path", "query", "querystring", "header", "cookie", "body" or "form_data") and name, which is the media type for "body"; add keyword, a JSON pointer into the parameter schema, for one keyword.
  • Response: status; add keyword for a response body keyword, and media_type when the response declares more than one.

Returns: List of dictionaries with keys:

  • label - Label name
  • reached - "valid", "invalid" or "both": whether the label sent values that satisfy the element, violate it, or both

Raises: ValueError when no such element exists in the specification.

Example

labels = coverage.covered_by(
    "GET",
    "/test",
    location="query",
    name="id",
    keyword="/minLength",
)
for entry in labels:
    print(entry["label"], entry["reached"])

label_summary / label_summaries

label_summary(label) / label_summaries()

Coverage of one label, in the shape of statistic(), or None when the label has no recorded traffic. label_summaries() maps every label to its summary. Professional only.

Returns: LabelSummary | None / dict[str, LabelSummary]

label_overflow

label_overflow()

New labels dropped after the map reached 100,000 distinct labels, counted once per request that carried one. A non-zero value means some label summaries are missing.

Returns: int

requests

requests

Integration for the requests library.

Returns: RequestsPlugin instance.

httpx

httpx

Integration for the httpx library.

Returns: HttpxPlugin instance.

postman

postman

Import traffic from Postman Collections (Professional).

Returns: PostmanPlugin instance.

har

har

Import traffic from HAR files (Professional).

Returns: HarPlugin instance.

vcr

vcr

Import traffic from VCR cassettes (Professional).

Returns: VcrPlugin instance.

django

django

Integration for Django test clients.

Returns: DjangoPlugin instance.

flask

flask

Integration for Flask test clients.

Returns: FlaskPlugin instance.


RequestsPlugin

RequestsPlugin

Integration for the requests library.

track_session

track_session(session)

Attach coverage tracking to a requests Session.

Parameters:

Name Type Description Default
session requests.Session required

Returns: The same session with coverage tracking enabled.

Example

import requests

session = coverage.requests.track_session(requests.Session())
response = session.get("https://api.example.com/users")

record

record(request, response=None, timestamp=None)

Record a request/response pair.

Parameters:

Name Type Description Default
request requests.PreparedRequest required
response requests.Response | None None
timestamp float | None Unix timestamp of the interaction None

Example

import requests

response = requests.get("https://api.example.com/users")
coverage.requests.record(request=response.request, response=response)

response_hook

response_hook()

Create a response hook for manual session configuration.

Returns: A ResponseHook callable.

Example

import requests

hook = coverage.requests.response_hook()
response = requests.get(
    "https://api.example.com/users",
    hooks={"response": [hook]}
)

HttpxPlugin

HttpxPlugin

Integration for the httpx library.

track_client

track_client(client)

Attach coverage tracking to an httpx Client.

Parameters:

Name Type Description Default
client httpx.Client | httpx.AsyncClient required

Returns: The same client with coverage tracking enabled.

Example

import httpx

# Synchronous
client = coverage.httpx.track_client(httpx.Client())
response = client.get("https://api.example.com/users")

# Asynchronous
async def fetch_users():
    async_client = coverage.httpx.track_client(httpx.AsyncClient())
    return await async_client.get("https://api.example.com/users")

record

record(request, response=None, timestamp=None)

Record a request/response pair.

Parameters:

Name Type Description Default
request httpx.Request required
response httpx.Response | None None
timestamp float | None Unix timestamp of the interaction None

response_hook

response_hook()

Create a synchronous response hook.

Returns: A SyncResponseHook callable.

async_response_hook

async_response_hook()

Create an asynchronous response hook.

Returns: An AsyncResponseHook callable.


PostmanPlugin

PostmanPlugin

Professional Edition

Postman, HAR, and VCR import require TraceCov Professional. In the Community edition installed by pip install tracecov, their methods raise ValueError.

Import traffic from Postman Collections (Professional).

record_from_path

record_from_path(path)

Import traffic from a Postman Collection file.

Parameters:

Name Type Description Default
path str | Path Path to Postman Collection JSON required

Example

coverage.postman.record_from_path("collection.json")

record_from_dict

record_from_dict(data)

Import traffic from a parsed Postman Collection.

Parameters:

Name Type Description Default
data dict Postman Collection as dictionary required

Supported formats: Postman Collection v1.0, v2.0, v2.1


HarPlugin

HarPlugin

Import traffic from HAR files (Professional).

record_from_path

record_from_path(path)

Import traffic from a HAR file.

Parameters:

Name Type Description Default
path str | Path Path to HAR file required

Example

coverage.har.record_from_path("traffic.har")

record_from_dict

record_from_dict(archive)

Import traffic from a parsed HAR archive.

Parameters:

Name Type Description Default
archive dict HAR archive as dictionary required

Supported formats: HAR v1.2, v1.3 from Chrome, Firefox, Edge, Postman.


VcrPlugin

VcrPlugin

Import traffic from VCR cassettes (Professional).

record_from_path

record_from_path(path)

Import traffic from a VCR cassette file.

Parameters:

Name Type Description Default
path str | Path Path to VCR cassette (YAML/JSON) required

record_from_dict

record_from_dict(cassette)

Import traffic from a parsed VCR cassette.

Parameters:

Name Type Description Default
cassette dict VCR cassette as dictionary required

Supported formats: Ruby VCR, Schemathesis cassettes.


DjangoPlugin

DjangoPlugin

Integration for Django test clients.

track_client

track_client(client)

Attach coverage tracking to a Django test client.

Parameters:

Name Type Description Default
client django.test.Client | django.test.AsyncClient | rest_framework.test.APIClient Django or DRF test client required

Returns: The same client with coverage tracking enabled.

Example

from django.test import Client

client = coverage.django.track_client(Client())
response = client.get("/api/users/")

record

record(request, response=None, timestamp=None, elapsed=None)

Record a Django request/response pair.

Parameters:

Name Type Description Default
request django.http.HttpRequest Django request object required
response django.http.HttpResponse | None Django response object None
timestamp float | None Unix timestamp of the interaction None
elapsed float | None Response time in seconds None

FlaskPlugin

FlaskPlugin

Integration for Flask test clients.

track_client

track_client(client)

Attach coverage tracking to a Flask test client.

Parameters:

Name Type Description Default
client flask.testing.FlaskClient | werkzeug.test.Client Flask or Werkzeug test client required

Returns: The same client with coverage tracking enabled.

Example

from flask import Flask

app = Flask("myapp")
client = coverage.flask.track_client(app.test_client())
response = client.get("/api/users/")

record

record(request, response=None, timestamp=None, elapsed=None)

Record a Flask request/response pair.

Parameters:

Name Type Description Default
request werkzeug.wrappers.Request Flask/Werkzeug request object required
response werkzeug.wrappers.Response | None Flask/Werkzeug response object None
timestamp float | None Unix timestamp of the interaction None
elapsed float | None Response time in seconds None

Low-Level Types

HttpRequest

HttpRequest(method, url, body=None, headers=None)

HTTP request for coverage tracking.

Parameters:

Name Type Description Default
method str HTTP method (GET, POST, etc.) required
url str Full URL including query parameters required
body bytes | None Request body None
headers dict | None Request headers None

HttpResponse

HttpResponse(status_code, elapsed, body=None, headers=None)

HTTP response for coverage tracking. Without body and headers, response schema coverage is not measured.

Parameters:

Name Type Description Default
status_code int HTTP status code required
elapsed float Response time in seconds required
body bytes | None Response body None
headers dict | None Response headers, including Content-Type None

HttpInteraction

HttpInteraction(request, response, timestamp)

A request, its response, and when it happened.

Parameters:

Name Type Description Default
request HttpRequest Request required
response HttpResponse | None Response, None when none was received required
timestamp float Unix timestamp of the interaction required

parse_event_label

parse_event_label(label)

Parse a coverage event label, as returned by record_schemathesis_interactions(), into a CoverageEvent. Labels are |-separated: tracecov|{domain}|{method}|{path}|{location}|{parameter}|{schema_path}.

Raises: ValueError when the label does not have seven segments or does not start with tracecov.

Example

import tracecov

event = tracecov.parse_event_label("tracecov|schema|GET|/users|query|limit|/minimum")
print(event.parameter, event.schema_path)  # limit /minimum

labeled

labeled(*labels)

Context manager that credits every interaction recorded inside it to labels, on top of the labels already active. The labels follow the code into asyncio tasks but not into new threads. See Which Tests Cover What. Professional only; the Community edition emits TracecovEditionWarning on each recording and ignores the labels.

Example

import tracecov

with tracecov.labeled("suite:smoke"):
    coverage.record(
        tracecov.HttpRequest(
            method="GET", url="https://api.example.com/test?id=1"
        ),
        tracecov.HttpResponse(status_code=200, elapsed=0.1),
    )

CoverageEvent

CoverageEvent

Frozen dataclass returned by parse_event_label(), with the label's segments as str attributes: namespace, domain, method, path, location, parameter, schema_path.