Modern QA2026The Four Phases — tiles
Log inJoin
2 / 108 · 03 Agentic Testing Architectures · The ReAct Core Loop for Testing← prev⊞ allnext →☰ Read as one page

1.2The Four Phases

+---------------------------------------------------+
|                                                   |
|  OBSERVE --> THINK --> ACT --> EVALUATE --> LOOP  |
|     |           |         |          |            |
|  Read page   Decide    Execute    Check if        |
|  state,      what to   the test   assertion       |
|  logs,       test      action     passed or       |
|  errors      next                 failed          |
|                                                   |
+---------------------------------------------------+

Phase 1: OBSERVE

The agent gathers information about the current state of the system under test. In browser testing, this means reading the page content, URL, console errors, and network activity. In API testing, it means reading response status codes, headers, and bodies.

class Observation:
    url: str                    # Current page URL
    text: str                   # Visible text content (truncated)
    errors: list[str]           # Console errors
    network_requests: list[dict]  # Recent HTTP requests
    screenshot_path: str | None   # Path to current screenshot
    page_title: str              # Document title

Key insight: The quality of the observation determines the quality of the agent's reasoning. A rich observation (text + errors + network) produces better decisions than a sparse one (just the URL).

Phase 2: THINK

The agent analyzes the observation and decides what to do next. This is where the LLM's reasoning ability is essential. It considers:

  • The test objective (what are we trying to verify?)
  • The current state (where are we now?)
  • The history (what have we already tried?)
  • The constraints (how many steps remain? what actions are allowed?)
prompt = f"""
Objective: {test_objective}
Current URL: {observation.url}
Page content (truncated): {observation.text[:2000]}
Console errors: {observation.errors}
History: {self.history[-5:]}  # Last 5 actions

What should I do next? Choose one:
- NAVIGATE <url>
- CLICK <selector>
- TYPE <selector> <text>
- ASSERT <condition>
- DONE <pass|fail> <reason>
"""
decision = self.llm.generate(prompt)

Phase 3: ACT

The agent executes the decided action against the system under test. This is the only phase that changes state.

def execute(self, decision: str) -> ActionResult:
    if decision.startswith("NAVIGATE"):
        url = decision.split(" ", 1)[1]
        self.browser.navigate(url)
    elif decision.startswith("CLICK"):
        selector = decision.split(" ", 1)[1]
        self.browser.click(selector)
    elif decision.startswith("TYPE"):
        parts = decision.split(" ", 2)
        self.browser.type(parts[1], parts[2])
    elif decision.startswith("ASSERT"):
        condition = decision.split(" ", 1)[1]
        return self.evaluate_assertion(condition)
    elif decision.startswith("DONE"):
        return self.parse_final_result(decision)

Phase 4: EVALUATE

After acting, the agent evaluates whether the action succeeded and whether the test objective is met. If the objective is not yet met, the loop continues.

if result.is_terminal:
    return TestResult(
        status=result.status,
        reason=result.reason,
        steps_taken=self.step_count,
        history=self.history
    )
# Otherwise, loop back to OBSERVE