CtrlK
BlogDocsLog inGet started
Tessl Logo

golikovichev/postman2pytest

Convert a Postman Collection v2.1 JSON file into a runnable pytest test suite using the postman2pytest CLI. Use when the user has a Postman collection (a .postman_collection.json or v2.1 JSON export) and wants to run it as pytest in CI, when migrating from Postman/Newman to a Python-native test stack, when bridging Postman-documented APIs into a pytest-based regression suite, when the user asks to generate pytest tests from Postman, or when the user mentions wanting to keep Postman as the source of truth but run the suite with pytest.

93

1.00x
Quality

100%

Does it follow best practices?

Impact

100%

1.00x

Average score across 2 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

main.py

"""
postman2pytest: convert a Postman Collection v2.1 into executable pytest tests.

Usage:
    python main.py --collection data/my_api.postman_collection.json --out generated_tests/test_api.py
"""

import argparse
import json
import logging
import sys
from importlib.metadata import PackageNotFoundError, version
from pathlib import Path

from core.generator import generate
from core.parser import ParsedRequest, parse_collection

try:
    __version__ = version("postman2pytest")
except PackageNotFoundError:
    __version__ = "1.0.2"

logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
logger = logging.getLogger(__name__)


def filter_requests_by_folder(
    requests: list[ParsedRequest], folder_name: str
) -> list[ParsedRequest]:
    """Return requests whose parsed folder slug matches the requested name."""
    normalized = folder_name.casefold()
    return [
        request
        for request in requests
        if request.folder and request.folder.casefold() == normalized
    ]


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Convert a Postman Collection v2.1 into executable pytest tests."
    )
    parser.add_argument("--version", action="version", version=f"postman2pytest {__version__}")
    parser.add_argument(
        "--collection", required=True, help="Path to Postman Collection JSON file (.json)"
    )
    parser.add_argument(
        "--out",
        required=True,
        help="Output path for the generated pytest file (e.g. generated_tests/test_api.py)",
    )
    parser.add_argument(
        "--base-url",
        help="Override BASE_URL in generated tests (default: reads from BASE_URL env var)",
    )
    parser.add_argument(
        "--filter-folder",
        help="Only generate tests for requests in the named Postman folder (case-insensitive)",
    )
    parser.add_argument(
        "--max-input-mb",
        type=int,
        default=100,
        help="Refuse to load collections larger than this many MB (default: 100)",
    )
    args = parser.parse_args()

    collection_path = Path(args.collection)
    if not collection_path.exists():
        logger.error("Collection file not found: %s", collection_path)
        return 1

    size_mb = collection_path.stat().st_size / (1024 * 1024)
    if size_mb > args.max_input_mb:
        logger.error(
            "Collection file is %.1f MB, larger than --max-input-mb %d. Refusing to load.",
            size_mb,
            args.max_input_mb,
        )
        return 1

    try:
        collection_name = (
            json.loads(collection_path.read_text(encoding="utf-8"))
            .get("info", {})
            .get("name", collection_path.stem)
        )
    except (json.JSONDecodeError, OSError, KeyError) as exc:
        logger.warning(
            "Could not read collection name from %s: %s. Falling back to file stem.",
            collection_path,
            exc,
        )
        collection_name = collection_path.stem

    requests = parse_collection(collection_path)
    if args.filter_folder:
        requests = filter_requests_by_folder(requests, args.filter_folder)
        if not requests:
            logger.error("Folder not found or contains no valid requests: %s", args.filter_folder)
            return 1

    if not requests:
        logger.error("No valid requests found in collection. Nothing to generate.")
        return 1

    output_path = Path(args.out)
    generate(requests, collection_name=collection_name, output_path=output_path)

    print(f"\nGenerated {len(requests)} test(s) -> {output_path}")
    print(f"  Run with: pytest {output_path} -v")
    if args.base_url:
        print(f"  Tip: BASE_URL={args.base_url} pytest {output_path} -v")
    else:
        print("  Tip: set BASE_URL env var to point at your API")
    return 0


if __name__ == "__main__":
    sys.exit(main())

CHANGELOG.md

CONTRIBUTING.md

main.py

README.md

REFERENCE.md

SECURITY.md

SKILL.md

tessl.json

tile.json