AI coding agents can now generate application code much faster than traditional development workflows.
That creates a new problem for engineering teams.
If implementation becomes faster, testing cannot remain entirely dependent on manually written end-to-end scenarios.
A modern QA pipeline needs to answer a different question:
Can an AI system plan, execute, analyze, and improve browser-based tests without removing the deterministic controls that make automated testing reliable?
This is where agentic QA becomes useful.
A practical agentic QA system does not replace browser automation with an LLM. Instead, it places an AI reasoning layer around a deterministic browser automation engine.
The architecture looks like this:
Requirement / Code Change
|
v
QA Planning Agent
|
v
Test Scenarios
|
v
Browser Automation Engine
|
v
Application
|
v
Test Results / Trace
|
v
Analysis Agent
|
+----+----+
| |
v v
Bug Test Fix
Report Recommendation
For .NET teams, this approach can be implemented using C#, browser automation, test frameworks, APIs, CI/CD pipelines, and an AI orchestration layer.
The key is to keep reasoning and execution separate.
What Makes QA "Agentic"?
Traditional automated testing follows a predefined path:
Open application
|
Login
|
Open Orders
|
Select order
|
Verify status
The test already knows what to do.
An agentic workflow introduces a planning layer:
Requirement
|
v
Agent determines scenarios
|
v
Agent selects available test actions
|
v
Browser executes actions
|
v
Agent evaluates results
|
v
Next action
The agent can adapt its next action based on what it observes.
However, that does not mean every test should become autonomous.
Stable business-critical workflows are often better represented as deterministic automated tests.
Agentic QA is most useful where exploration, test generation, failure analysis, and adaptive validation provide additional value.
Deterministic Automation vs Agentic QA
| Area | Traditional Automation | Agentic QA |
|---|---|---|
| Test path | Predefined | Dynamically planned |
| Locators | Explicit | Can be discovered or selected |
| Assertions | Predefined | Can be generated and evaluated |
| Exploration | Limited | Stronger |
| Execution | Deterministic | Adaptive |
| Failure analysis | Developer/QA driven | AI-assisted |
| Repeatability | Very high | Depends on agent controls |
| Cost | Predictable | Includes model usage |
| Best use | Critical regression | Exploration and adaptive scenarios |
The two approaches should normally coexist.
A mature pipeline might use:
QA Pipeline
|
+----------+----------+
| |
v v
Deterministic Tests Agentic Tests
| |
v v
Critical Regression Exploration
API Contracts Failure Analysis
Smoke Tests Scenario Generation
Security Regression Change-Based Testing
Use the Browser Engine as the Execution Kernel
An AI model should not directly control every browser operation through unrestricted instructions.
Instead, expose a controlled browser automation layer.
For .NET, a browser automation framework can provide operations such as:
await page.GotoAsync(url);
await page
.GetByRole(AriaRole.Button,
new() { Name = "Sign in" })
.ClickAsync();
await page
.GetByLabel("Email")
.FillAsync(email);
The browser engine handles:
Navigation
Locators
Clicks
Form interaction
Waiting
Screenshots
Network activity
Browser contexts
Assertions
Tracing
Modern browser automation frameworks also provide automatic waiting and web-aware assertions, reducing the need for arbitrary delays in tests.
That is important because an AI agent should not have to reason about every low-level browser timing problem.
The Four Main Agentic QA Components
A practical system can be divided into four components.
1. Planner
Converts a requirement into test scenarios.
Requirement
|
v
Planner
|
+--> Happy Path
+--> Validation
+--> Authorization
+--> Boundary Cases
+--> Error Handling
2. Executor
Translates approved test actions into browser operations.
3. Analyzer
Examines:
Test output
Screenshots
Browser traces
Console errors
Network failures
Application logs
4. Reporter
Produces a structured result:
{
"status": "failed",
"scenario": "Checkout with invalid card",
"failureType": "validation",
"evidence": [
"Validation message missing"
],
"confidence": "high"
}
This separation makes the system easier to test and operate.
Start With a Structured Test Plan
Do not give an agent a vague instruction such as:
Test the checkout page.
Generate a structured plan instead:
{
"feature": "Checkout",
"scenarios": [
{
"name": "Successful checkout",
"priority": "critical"
},
{
"name": "Invalid card",
"priority": "high"
},
{
"name": "Missing billing address",
"priority": "medium"
}
]
}
The planner can create this structure.
The executor should consume the structure.
This creates a boundary between:
AI reasoning
and:
Browser execution
Define an Action Contract
The agent should not be able to execute arbitrary code.
Define an action model:
public sealed record BrowserAction
{
public required string Action { get; init; }
public string? Target { get; init; }
public string? Value { get; init; }
}
Supported actions might include:
goto
click
fill
select
check
uncheck
assertVisible
assertText
screenshot
The executor validates the action before executing it.
For example:
public async Task ExecuteAsync(
BrowserAction action,
IPage page)
{
switch (action.Action)
{
case "goto":
await page.GotoAsync(
action.Target!);
break;
case "click":
await page
.GetByRole(
AriaRole.Button,
new()
{
Name = action.Target
})
.ClickAsync();
break;
default:
throw new InvalidOperationException(
$"Unsupported action: {action.Action}");
}
}
This provides a controlled execution surface.
Why a Restricted Action Set Matters
An unrestricted agent could potentially:
Execute arbitrary JavaScript
Read secrets
Access internal endpoints
Modify production data
Download sensitive files
Navigate outside the test environment
A controlled action layer reduces that risk.
The agent should have access only to operations required for testing.
Think of the executor as a sandbox:
AI Agent
|
v
Allowed Actions
|
v
Browser
not:
AI Agent
|
v
Arbitrary Machine Access
Build a .NET Test Project
A typical structure could be:
tests/
└── AgenticQa/
├── Agents/
│ ├── QaPlanner.cs
│ └── FailureAnalyzer.cs
├── Browser/
│ ├── BrowserExecutor.cs
│ └── BrowserAction.cs
├── Models/
│ ├── TestPlan.cs
│ └── TestResult.cs
├── Tests/
│ └── CheckoutTests.cs
└── Configuration/
└── TestSettings.cs
This keeps AI orchestration separate from browser execution.
The project can still use a standard .NET testing framework.
Example Browser Fixture
A reusable fixture can initialize the browser:
public sealed class BrowserFixture
: IAsyncLifetime
{
private IPlaywright? _playwright;
public IBrowser Browser { get; private set; } = null!;
public async Task InitializeAsync()
{
_playwright =
await Playwright.CreateAsync();
Browser =
await _playwright.Chromium
.LaunchAsync(new()
{
Headless = true
});
}
public async Task DisposeAsync()
{
await Browser.CloseAsync();
_playwright?.Dispose();
}
}
The browser lifecycle should be managed centrally.
This makes parallel execution and resource management easier to control.
Generate Tests From Requirements
Suppose the requirement is:
Users should be able to reset their password
using their registered email address.
The planner could produce:
Scenario 1
-----------
Valid registered email
Scenario 2
-----------
Unregistered email
Scenario 3
-----------
Invalid email format
Scenario 4
-----------
Empty email
Scenario 5
-----------
Expired reset link
This is where AI can add value.
It can expand a high-level requirement into candidate scenarios.
But the resulting scenarios should still be validated before execution.
Do Not Trust AI-Generated Assertions Automatically
An agent might generate:
Verify that checkout works.
That is too vague.
A useful assertion is specific:
await Expect(
page.GetByRole(
AriaRole.Heading,
new() { Name = "Order confirmed" }))
.ToBeVisibleAsync();
The test needs to define what successful behavior means.
A model can suggest assertions.
The test framework should execute explicit assertions.
This prevents a weak test from passing simply because the page loaded successfully.
Use Semantic Locators
Browser tests become fragile when they depend heavily on CSS implementation details.
Avoid:
await page
.Locator(".btn-primary:nth-child(2)")
.ClickAsync();
Prefer semantic locators:
await page
.GetByRole(
AriaRole.Button,
new() { Name = "Submit Order" })
.ClickAsync();
Or:
await page
.GetByTestId("submit-order")
.ClickAsync();
This is useful for agentic systems because the agent can reason about application semantics rather than purely visual coordinates.
Test Generation Should Be Followed by Validation
A safe workflow is:
Requirement
|
v
AI Generates Scenario
|
v
Human / Rule Validation
|
v
Structured Test Plan
|
v
Browser Execution
Do not immediately execute every generated scenario against a production environment.
Generated tests should first run against:
Local
Test
Staging
depending on the application's deployment strategy.
Add API Testing to the Same Pipeline
Browser testing is not always necessary.
If a test can be validated through an API, use the API.
For example:
Create Order
|
v
API
|
v
Database State
Then use browser automation only when UI behavior needs validation.
A combined pipeline can look like:
Agent Plan
|
+--> API Test
|
+--> Browser Test
|
+--> Database Verification
This can make the overall test suite more efficient.
Use API Calls to Prepare Test State
Suppose a browser test needs an existing customer.
Instead of creating the customer through ten UI interactions:
Login
|
Create customer
|
Fill address
|
Save
|
Verify
|
Open checkout
use an API or controlled fixture:
Test Setup
|
v
Create Customer via API
|
v
Browser Test
Browser tests should focus on the behavior they are intended to validate.
Agentic Failure Analysis
One of the strongest use cases is failure analysis.
Suppose a test fails:
Expected:
Order confirmed
Actual:
Payment failed
The analyzer can inspect:
Screenshot
+
DOM
+
Console logs
+
Network responses
+
Application logs
+
Trace
and classify the failure:
{
"category": "application",
"likelyCause": "payment API returned 500",
"testFailure": "expected confirmation page",
"confidence": 0.91
}
The AI should report the diagnosis rather than automatically modifying production code.
Capture Evidence
Every failed test should produce useful evidence.
At minimum:
Screenshot
DOM snapshot where appropriate
Browser console
Network information
Test log
Execution trace
For example:
await page.ScreenshotAsync(new()
{
Path = "artifacts/checkout-failure.png",
FullPage = true
});
Evidence is critical for both humans and AI analysis.
Without evidence, a failure-analysis agent is forced to guess.
Use Traces Instead of Screenshots Alone
A screenshot shows what the browser looked like.
It does not necessarily explain:
Which request failed?
Which action happened first?
What was the DOM state?
How long did the operation take?
A browser trace can provide richer execution information.
The pipeline becomes:
Test
|
v
Trace
|
+--> Screenshot
+--> Network
+--> Actions
+--> Timing
|
v
Failure Analyzer
This gives the agent structured evidence rather than a single image.
Detect Flaky Tests
A failed test does not always mean the application is broken.
A test may be flaky because of:

Join the conversation! Your thoughts help the community grow.