Skip to content

Discovery miner: Python test functions (pytest, unittest) #127

@Dimwiddle

Description

@Dimwiddle

Summary

Extract test functions from Python test files using tree-sitter. Produces DiscoveredItem(kind=TEST_FUNCTION) for each found test.

Depends on: #124, #125, #126

New file

src/specleft/discovery/miners/python/tests.py

import uuid
from specleft.discovery.models import SupportedLanguage, MinerResult, DiscoveredItem, ItemKind, TestFunctionMeta
from specleft.discovery.context import MinerContext

class PythonTestMiner:
    miner_id = uuid.UUID("a7b21db5-0d22-41be-9902-7c725e63892e")
    name = "python_test_functions"
    languages = frozenset({SupportedLanguage.PYTHON})

    def mine(self, ctx: MinerContext) -> MinerResult: ...

Detection rules

File targeting: use ctx.file_index.files_matching("test_*.py", "*_test.py") — do NOT walk the filesystem directly.

Tree-sitter queries (via ctx.registry.parse(file_path)):

  • Top-level functions where name starts with test_
  • Methods inside a class where the class name starts with Test

Framework detection: read from ctx.frameworks[SupportedLanguage.PYTHON] — do NOT re-detect. The FrameworkDetector has already resolved this.

Typed metadata

Each item's metadata dict must conform to TestFunctionMeta:

TestFunctionMeta(
    framework       = ctx.frameworks.get(SupportedLanguage.PYTHON, ["unknown"])[0],
    class_name      = "TestLogin" | None,
    decorators      = ["pytest.mark.parametrize"],
    has_docstring   = True,
    docstring       = "Test that valid credentials...",
    is_parametrized = True,
)

Pass meta.model_dump() as the metadata dict when constructing DiscoveredItem.

name: raw function name (e.g. test_valid_credentials)
language: SupportedLanguage.PYTHON
confidence: 0.9 for test_ prefix + known framework; 0.7 otherwise

Error handling

Return MinerResult(error_kind=MinerErrorKind.PARSE_ERROR, error="...") for files that fail to parse, not exceptions. Continue processing remaining files.

Acceptance criteria

  • Given a fixture with 3 test functions (1 plain, 1 parametrized, 1 inside TestCase), miner returns exactly 3 items
  • All items have language=SupportedLanguage.PYTHON
  • Each item's metadata validates against TestFunctionMeta (no ValidationError)
  • is_parametrized=True set correctly for the parametrized function
  • class_name populated for the TestCase method, None for top-level functions
  • Uses ctx.file_index.files_matching() — does not walk filesystem
  • Uses ctx.frameworks — does not re-detect framework
  • Parse errors on individual files produce error_kind=PARSE_ERROR on the result, not exceptions
  • Tests in tests/discovery/miners/test_python_tests.py using fixtures from tests/fixtures/discovery/
  • Update scenarios and tests in features/feature-spec-discovery.md to cover the functionality introduced by this issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    minerDiscovery miner implementationnew featureIssues or PRs for a new feature that doesn't currently existpythonPython language specific

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions