Skip to main content

A lightweight Python unit testing framework with decorator-based test collection and execution.

Project description

PyForge Testing Framework

A lightweight Python testing framework with decorator-based test collection and automatic discovery—designed for personal projects and learning.

PyPI version Python 3.12+ License: MIT Code Quality Type Checked

Note: PyForge is designed for personal projects, learning, and small-scale testing. It is not a substitute for production frameworks like pytest.

Features

  • 🎯 Simple Decorators — Mark tests with @test, automatic collection
  • 🚀 Zero Configuration — Works out of the box
  • 📦 Zero Dependencies — Pure Python, nothing to install but PyForge itself
  • 🔍 Auto-Discovery — Finds and loads all test*.py files automatically
  • Fast Execution — Minimal overhead, quick feedback
  • 🏷️ Test Markers — Organize tests by priority (integration, slow)
  • 📊 Parameterized Tests — Run one test with multiple inputs
  • Skip Tests — Conditionally skip tests with clear reasons
  • Full Type Hints — PEP 484 type annotations throughout
  • 🎨 Clean Output — Color-coded results with minimal internal noise

Installation

pip install pyforge-test

Or from GitHub:

pip install git+https://github.com/ertanturk/pyforge-test.git

Quick Start

1. Create Test File

mkdir -p tests && touch tests/__init__.py

Create tests/test_example.py:

from pyforge_test import test


@test
def test_addition() -> None:
    """Test basic arithmetic."""
    assert 2 + 2 == 4


@test
def test_strings() -> None:
    """Test string manipulation."""
    assert "hello".upper() == "HELLO"

2. Run Tests

pyforge

Expected output:

Discovering test modules in '/path/to/tests'...
Loaded: test_example.py

Loaded 1 test module(s).

Executing 2 test(s).

PyForge Test Results
------------------------------------------------------------------------

test_example.py
  PASSED test_addition (Line 4)
  PASSED test_strings (Line 9)

------------------------------------------------------------------------
Summary: PASSED: 2/2  FAILED: 0/2  SKIPPED: 0/2  ERRORS: 0/2
Took 5 ms to execute all tests
------------------------------------------------------------------------

CLI Options

pyforge              # Run all tests
pyforge -q           # Quiet: only failures
pyforge -v           # Verbose: full tracebacks
pyforge --fail-fast  # Stop on first failure
pyforge -k api       # Filter by test name
pyforge test_api.py  # Run specific file

Test Features

Basic Tests

from pyforge_test import test


@test
def test_example() -> None:
    """Test function requirements:
    - Starts with 'test_'
    - No parameters
    - Return type -> None
    - Uses @test decorator
    """
    assert True

Parameterized Tests

Run the same test with multiple inputs:

from pyforge_test import test_parameterized


@test_parameterized([
    (2, 3, 5),
    (10, 5, 15),
    (100, 200, 300),
])
def test_addition(a: int, b: int, expected: int) -> None:
    """Generates: test_addition_0, test_addition_1, test_addition_2"""
    assert a + b == expected

Test Markers

Execution priority: Unmarked (0)Integration (1)Slow (2)

from pyforge_test import test, test_marker

# Fast unit test (runs first)
@test
def test_fast() -> None:
    assert 2 + 2 == 4

# Integration test (requires external resources)
@test_marker("integration")
@test
def test_database() -> None:
    db.connect()
    assert db.is_connected()

# Slow test (performance-intensive)
@test_marker("slow")
@test
def test_large_dataset() -> None:
    result = process_records(1_000_000)
    assert len(result) == 1_000_000

Important: @test_marker must come before @test

Skip Tests

import sys
from pyforge_test import test, test_skip, test_skipif


@test_skip(reason="Not implemented yet")
def test_future() -> None:
    """Always skipped."""
    pass


@test_skipif(sys.platform == "win32", reason="Unix only")
def test_unix() -> None:
    """Skipped on Windows."""
    pass

Project Structure

my-project/
├── src/
│   ├── main.py
│   └── utils.py
├── tests/
│   ├── __init__.py            # Required (can be empty)
│   ├── test_main.py           # Auto-discovered
│   └── test_utils.py          # Auto-discovered
├── README.md
└── pyproject.toml

Code Quality

PyForge is fully type-checked and linted:

  • Ruff: All checks passing ✅
  • Pylint: 9.89/10 score ✅
  • Pyright: Strict type checking ✅
  • Type Hints: Full PEP 484 compliance ✅

Documentation

Development

Code standards:

  • PEP 484 type hints on all functions
  • Google-style docstrings
  • Exception chaining: raise ... from e
  • No bare except statements

License

MIT — See LICENSE

Contributing

Contributions welcome! Open an issue or PR on GitHub.


Status: Alpha (v0.2.0) | Python: 3.12+ | Type Safe: Yes | Dependencies: 0

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

pyforge_test-0.2.0.tar.gz (24.0 kB view details)

Uploaded Source

Built Distribution

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

pyforge_test-0.2.0-py3-none-any.whl (18.0 kB view details)

Uploaded Python 3

File details

Details for the file pyforge_test-0.2.0.tar.gz.

File metadata

  • Download URL: pyforge_test-0.2.0.tar.gz
  • Upload date:
  • Size: 24.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for pyforge_test-0.2.0.tar.gz
Algorithm Hash digest
SHA256 eaed0b11d42aa91e6d7b3b42cdc04accc62db08268875517af2d5e0585245920
MD5 3e0a8469fe1fa99602114c9dab8c9b58
BLAKE2b-256 ebeae0f5ae1765065fa8a23e2f4aadf9a22658d3ff98308c77e94209908dab71

See more details on using hashes here.

File details

Details for the file pyforge_test-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: pyforge_test-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 18.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for pyforge_test-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2f89206ac3ee1be49099df6b1332f22117765554a6deb661d8369fce2afedebc
MD5 ca2239f09ca32ff3ed5d15ed306589d0
BLAKE2b-256 1a243e2937332a7a3aa12584da83818a10e26c22678689449de26943ffd93fab

See more details on using hashes here.

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