orchestrata_tools.test_generator.branch_tests
Reference › orchestrata_tools › test_generator › branch_tests
This module identifies branches in a method from its docstring and metadata and generates a test for each. Detection is heuristic: it reads the Raises and Returns sections and does not look at the method body. For body-level branch tests see testgen.branch. It is used by combined_generator and re-exported by the package. tests/test_orchestrata_tools_branch_tests_coverage.py and tests/test_orchestrata_tools.py cover it.
Branch is a dataclass with a name, a condition and a suggestion. BranchTestGenerator.branches returns the branches found for a MethodInfo, and generate_for_method returns test source covering all of them, or an empty string when none are found.
At a glance
| Source | src/orchestrata_tools/test_generator/branch_tests.py |
| Lines of code | 115 |
| Classes | 2 |
| Functions | 0 |
| Imports (internal) | orchestrata_tools.models.ast_models |
| Imported by | orchestrata_tools.test_generator, orchestrata_tools.test_generator.combined_generator |
| Tests that import it | tests/test_orchestrata_tools.py, tests/test_orchestrata_tools_branch_tests_coverage.py |
Class diagram
Dependency diagram
API
Classes
Branch (dataclass)
Decorators: dataclass · Line 17
A single branch in a method.
| Attribute | Type | Default |
|---|---|---|
name |
str |
|
condition |
str |
|
suggestion |
str |
BranchTestGenerator
Line 25
Generate tests covering branches identified from method metadata.
| Method | Signature | Summary |
|---|---|---|
branches |
branches(method: MethodInfo) -> list[Branch] |
Identify branches from docstring and method metadata. |
generate_for_method |
generate_for_method(method: MethodInfo) -> str |
Generate tests covering all identified branches. |
Source
"""Analyze method docstrings to identify branches and generate test cases.
Heuristic-based branch detection from docstring Raises/Returns sections.
"""
from __future__ import annotations
import re
from dataclasses import dataclass
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from orchestrata_tools.models.ast_models import MethodInfo
@dataclass
class Branch:
"""A single branch in a method."""
name: str
condition: str
suggestion: str
class BranchTestGenerator:
"""Generate tests covering branches identified from method metadata."""
def branches(self, method: MethodInfo) -> list[Branch]:
"""Identify branches from docstring and method metadata."""
result = []
doc = method.docstring or ""
# Look for Raises: section
raises = re.findall(
r"Raises?:\s*\n?((?:\s*[-*]?\s*\w+.*\n?)+)", doc, re.IGNORECASE
)
if raises:
for line in raises[0].split("\n"):
match = re.match(
r"\s*[-*]?\s*(\w+Error|\w+Exception|\w+):?\s*(.*)", line
)
if match:
exc_type, desc = match.groups()
result.append(
Branch(
name=f"raises_{exc_type.lower()}",
condition=f"{exc_type} raised",
suggestion=desc
or f"Test that {exc_type} is raised on invalid input",
)
)
# Look for Returns: section with multiple return types
returns = re.findall(
r"Returns?:\s*\n?((?:\s*.*?\n?)+?)(?=\n\w+:|$)", doc, re.IGNORECASE
)
if returns:
ret_text = returns[0]
if "None" in ret_text or "null" in ret_text.lower():
result.append(
Branch(
name="returns_none",
condition="returns None",
suggestion="Test case where method returns None",
)
)
# Async methods have success/failure branches
if method.is_async:
result.append(
Branch(
name="async_success",
condition="async operation succeeds",
suggestion="Test successful async execution",
)
)
# Methods with optional parameters have default branch
for param in method.parameters:
if param.default:
result.append(
Branch(
name=f"default_{param.name}",
condition=f"{param.name} uses default ({param.default})",
suggestion=f"Test with default value for {param.name}",
)
)
return result
def generate_for_method(self, method: MethodInfo) -> str:
"""Generate tests covering all identified branches."""
branches = self.branches(method)
if not branches:
return ""
lines = [
f"class Test{method.class_name or ''}{method.name.title()}Branches:",
f' """Branch coverage tests for {method.class_name}.{method.name}()."""',
"",
]
for branch in branches:
test_name = f"test_{branch.name}"
lines.extend(
[
f" def {test_name}(self):",
f' """Branch: {branch.condition}."""',
f" # {branch.suggestion}",
" raise NotImplementedError('TODO: implement branch test')",
"",
]
)
return "\n".join(lines)