惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Proofpoint News Feed
U
Unit 42
V
Visual Studio Blog
D
DataBreaches.Net
F
Fortinet All Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The GitHub Blog
The GitHub Blog
Y
Y Combinator Blog
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
T
The Blog of Author Tim Ferriss
GbyAI
GbyAI
博客园 - 叶小钗
Blog — PlanetScale
Blog — PlanetScale
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
MongoDB | Blog
MongoDB | Blog
The Cloudflare Blog
云风的 BLOG
云风的 BLOG
D
Docker
G
Google Developers Blog
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
S
SegmentFault 最新的问题

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Testing AI-Powered Applications: Strategies for LLM Integ...
ZNY · 2026-05-16 · via DEV Community

Testing AI applications is fundamentally different from testing traditional software. There's no deterministic output, prompts change behavior, and edge cases multiply. Here's how to build a robust testing strategy for AI-powered applications.

The AI Testing Challenge

Traditional testing:

Input → Function → Expected Output

AI testing:

Input → Prompt + Context → Probabilistic Output

You can't assert exact outputs. Instead, you test properties.

Property-Based Testing for AI

`typescript
// Instead of testing exact output, test properties

interface TestCase {
input: string;
constraints: Constraint[];
}

interface Constraint {
type: 'contains' | 'excludes' | 'length' | 'format' | 'json';
value: string | number | RegExp;
}

async function testAIOutput(testCase: TestCase, actualOutput: string): Promise {
for (const constraint of testCase.constraints) {
switch (constraint.type) {
case 'contains':
if (!actualOutput.includes(constraint.value as string)) return false;
break;
case 'excludes':
if (actualOutput.includes(constraint.value as string)) return false;
break;
case 'length':
if (actualOutput.length > (constraint.value as number)) return false;
break;
case 'json':
try {
JSON.parse(actualOutput);
} catch {
return false;
}
break;
}
}
return true;
}

// Example test
const testCase: TestCase = {
input: 'Extract the name and email from: John Doe, john@example.com',
constraints: [
{ type: 'contains', value: 'John' },
{ type: 'contains', value: 'john@example.com' },
{ type: 'excludes', value: 'undefined' },
{ type: 'length', value: 100 }
]
};
`

Prompt Versioning and Regression Testing

`python
import hashlib
from datetime import datetime

class PromptRegistry:
def init(self):
self.prompts = {}

def register(self, name: str, version: str, prompt: str, test_cases: list):
key = f"{name}:{version}"
self.prompts[key] = {
'prompt': prompt,
'testcases': testcases,
'hash': hashlib.md5(prompt.encode()).hexdigest(),
'registered': datetime.now()
}

def get_prompt(self, name: str, version: str) -> str:
return self.prompts[f"{name}:{version}"]['prompt']

def regressiontest(self, name: str, newversion: str,
llm_client, threshold: float = 0.8) -> bool:
"""Ensure new version passes existing test cases."""
old_prompt = self.prompts.get(f"{name}:{version}")
if not old_prompt:
return True

old_passes = 0
new_passes = 0

for tc in oldprompt['testcases']:
oldresult = await llmclient.complete(old_prompt['prompt'] + tc['input'])
newresult = await llmclient.complete(
self.getprompt(name, newversion) + tc['input']
)

oldok = await testAIOutput(tc, oldresult)
newok = await testAIOutput(tc, newresult)

if oldok: oldpasses += 1
if newok: newpasses += 1

New version should pass at least as many tests

return (newpasses / len(oldprompt['test_cases'])) >= threshold
`

Deterministic Output Testing

For structured outputs, test deterministically:

`typescript
import { z } from 'zod';

const CodeReviewSchema = z.object({
score: z.number().min(0).max(10),
issues: z.array(z.object({
severity: z.enum(['low', 'medium', 'high']),
line: z.number(),
description: z.string()
})),
summary: z.string()
});

async function testCodeReview(code: string, expectedScoreRange: [number, number]) {
const response = await llm.complete(
Review this code and return JSON: ${code}
);

// Parse and validate
const parsed = JSON.parse(response);
const validated = CodeReviewSchema.parse(parsed);

// Deterministic assertions
console.assert(
validated.score >= expectedScoreRange[0] &&
validated.score <= expectedScoreRange[1],
Score ${validated.score} outside expected range
);

console.assert(
validated.issues.length < 20,
'Too many issues reported'
);

return validated;
}
`

Mocking External AI Calls

`typescript
// For unit tests, mock the LLM client
class MockLLMClient {
constructor(private fixtures: Map) {}

async complete(prompt: string): Promise {
// Return fixture matching prompt pattern
for (const [pattern, response] of this.fixtures) {
if (prompt.includes(pattern)) {
return response;
}
}
return 'Mock response';
}

async *stream(prompt: string): AsyncGenerator {
const response = await this.complete(prompt);
for (const char of response) {
yield char;
}
}
}

// Usage in tests
const mockClient = new MockLLMClient(new Map([
['extract email', '{"email": "test@example.com"}'],
['summarize', 'This is a summary of the text.']
]));

// Now your business logic tests run fast and deterministically
`

Chaos Testing for AI Applications

`python
class AIChaosTests:
def testratelimits(self, client):
"""Does your app handle rate limits gracefully?"""
for _ in range(100):
try:
client.complete("test")
except RateLimitError:
assert client.retry_count > 0
break
else:
pytest.fail("Rate limit not encountered after 100 requests")

def testinvalidjson(self, client):
"""Does your app handle malformed JSON from LLM?"""

Inject bad response

client.mock_response('{"broken": }')
result = safeparsejson(client.complete("test"))
assert result is not None # Handled gracefully

def testemptycontext(self, client):
"""Does your app handle empty context?"""
result = client.complete("")
assert result is not None

def testmaxtokens_respected(self, client):
"""Does max_tokens actually limit output?"""
result = client.complete("test", max_tokens=10)
assert len(result) <= 50 # ~10 tokens
`

Integration Test Framework

`typescript
describe('AI Integration Tests', () => {
const client = new ClaudeClient(process.env.OFOXAPIKEY);

describe('Code Review Feature', () => {
it('identifies syntax errors', async () => {
const code = 'const x = ;';
const review = await reviewCode(client, code);
expect(review.issues.some(i => i.severity === 'high')).toBe(true);
});

it('handles valid code gracefully', async () => {
const code = 'const x = 42;';
const review = await reviewCode(client, code);
expect(review.issues.filter(i => i.severity === 'high')).toHaveLength(0);
});

it('respects max issues limit', async () => {
const code = '...'; // Large code
const review = await reviewCode(client, code, { maxIssues: 10 });
expect(review.issues.length).toBeLessThanOrEqual(10);
});
});
});
`

Building Testable AI Systems

  1. Separate concerns — Keep prompts in config, not buried in code
  2. Structured outputs — Use Zod/JSON Schema to constrain responses
  3. Fallback handling — Plan for API failures at every call site
  4. Snapshot testing — Store expected responses for regression

Getting Started

Build testable AI applications with ofox.ai — their API is reliable and consistent, making it easier to build deterministic test suites.

👉 Get started with ofox.ai

This article contains affiliate links.

Tags: testing,ai,programming,developer,quality
Canonical URL: https://dev.to/zny10289