Modern QA2026The ConstrainedTestHarness Class — tiles
Log inJoin
67 / 108 · 03 Agentic Testing Architectures · Production Test Harness Implementation← prev⊞ allnext →☰ Read as one page

9.2The ConstrainedTestHarness Class

from dataclasses import dataclass
from typing import Optional
import time

@dataclass
class HarnessConfig:
    max_steps: int = 30
    timeout_seconds: int = 300
    max_tokens: int = 50_000
    allowed_domains: list[str] = None
    allowed_actions: list[str] = None
    require_assertion: bool = True
    screenshot_on_failure: bool = True

class ConstrainedTestHarness:
    def __init__(self, agent, config: HarnessConfig):
        self.agent = agent
        self.config = config
        self.step_count = 0
        self.token_count = 0
        self.start_time = None
        self.violations = []

    def execute(self, test_objective: str) -> TestResult:
        self.start_time = time.time()

        while True:
            # Check all guardrails before each step
            violation = self.check_guardrails()
            if violation:
                return TestResult(
                    status="ABORTED",
                    reason=f"Guardrail violation: {violation}",
                    steps_taken=self.step_count,
                    violations=self.violations
                )

            # Let the agent take one step
            action = self.agent.next_action()

            # Validate the action before execution
            if not self.is_action_allowed(action):
                self.violations.append(f"Blocked action: {action}")
                return TestResult(
                    status="BLOCKED",
                    reason=f"Action not allowed: {action}",
                    steps_taken=self.step_count
                )

            # Execute and record
            result = self.agent.execute_action(action)
            self.step_count += 1
            self.token_count += result.tokens_used

            if result.is_terminal:
                # Validate the test result
                if self.config.require_assertion and not result.has_assertion:
                    return TestResult(
                        status="INVALID",
                        reason="Test completed without any assertion",
                        steps_taken=self.step_count
                    )
                return result

    def check_guardrails(self) -> Optional[str]:
        if self.step_count >= self.config.max_steps:
            return f"Max steps ({self.config.max_steps}) exceeded"
        elapsed = time.time() - self.start_time
        if elapsed > self.config.timeout_seconds:
            return f"Timeout ({self.config.timeout_seconds}s) exceeded"
        if self.token_count >= self.config.max_tokens:
            return f"Token budget ({self.config.max_tokens}) exhausted"
        return None

    def is_action_allowed(self, action) -> bool:
        if self.config.allowed_actions:
            if action.type not in self.config.allowed_actions:
                return False
        if self.config.allowed_domains and action.type == "NAVIGATE":
            from urllib.parse import urlparse
            domain = urlparse(action.url).netloc
            if domain not in self.config.allowed_domains:
                return False
        return True