Testing Overview
The ADK Utils Example includes two types of tests:
Unit Tests : Using Jest for testing individual functions and utilities
End-to-End Tests : Using Playwright for testing the full application flow
Unit Testing with Jest
Running Unit Tests
The project uses Jest for unit testing. All test scripts are defined in package.json:
# Run tests once
npm test
# Watch mode - reruns tests on file changes
npm run test:watch
# Generate coverage report
npm run test:coverage
Available Test Scripts
{
"scripts" : {
"test" : "jest" ,
"test:watch" : "jest --watchAll" ,
"test:coverage" : "jest --coverage"
}
}
Script Description testRuns all unit tests once test:watchRuns tests in watch mode, re-running on file changes test:coverageGenerates a code coverage report
Jest Configuration
The project uses ts-jest for TypeScript support:
{
"devDependencies" : {
"@types/jest" : "^30.0.0" ,
"jest" : "^30.2.0" ,
"ts-jest" : "^29.4.6"
}
}
Jest is configured to work seamlessly with TypeScript through the ts-jest preset.
Writing Unit Tests
Create test files with the .test.ts or .spec.ts extension:
import { describe , it , expect } from '@jest/globals' ;
describe ( 'Utility Functions' , () => {
it ( 'should process data correctly' , () => {
const result = processData ( 'input' );
expect ( result ). toBe ( 'expected output' );
});
});
End-to-End Testing with Playwright
Overview
Playwright tests simulate real user interactions in a browser environment. The test suite includes:
Home page functionality tests
Chat interaction tests with mock agents
API route mocking
Running E2E Tests
Headless Mode
Headed Mode
UI Mode
Run tests in headless mode (no visible browser): npm run test:e2e:headless
This runs tests with the --reporter=list flag for detailed output. Run tests with visible browser windows: Useful for debugging test failures by watching the browser interactions. Run tests with Playwright’s interactive UI: Provides a GUI for running, debugging, and inspecting tests.
E2E Test Scripts
{
"scripts" : {
"test:e2e:headless" : "playwright test --reporter=list" ,
"test:e2e:headed" : "playwright test --headed" ,
"test:e2e:ui" : "playwright test --ui"
}
}
Playwright Configuration
The playwright.config.ts file defines the test configuration:
import { defineConfig , devices } from "@playwright/test" ;
const PORT = process . env . PORT || 3000 ;
const baseURL = `http://localhost: ${ PORT } ` ;
export default defineConfig ({
testDir: "./e2e" ,
fullyParallel: true ,
forbidOnly: !! process . env . CI ,
retries: process . env . CI ? 2 : 0 ,
workers: process . env . CI ? 3 : undefined ,
reporter: "html" ,
use: {
baseURL ,
trace: "on-first-retry" ,
} ,
projects: [
{
name: "chromium" ,
use: { ... devices [ "Desktop Chrome" ] },
},
] ,
webServer: {
command: process . env . CI ? "npm run start" : "npm run dev" ,
url: baseURL ,
reuseExistingServer: ! process . env . CI ,
} ,
}) ;
Key Configuration Options
Option Value Description testDir./e2eDirectory containing test files fullyParalleltrueRun tests in parallel for faster execution retries2 (CI only)Retry failed tests in CI environments workers3 (CI only)Number of parallel workers reporterhtmlGenerate HTML test reports baseURLhttp://localhost:3000Base URL for test navigation
Playwright automatically starts the dev server before running tests using the webServer configuration.
Example Tests
Home Page Test
import { test , expect } from "@playwright/test" ;
test ( "has title" , async ({ page }) => {
await page . goto ( "/" );
// Expect a title "to contain" a substring.
await expect ( page ). toHaveTitle ( /ADK Agent Chat/ );
});
test ( "displays main content" , async ({ page }) => {
await page . goto ( "/" );
// Check for the main landmark
await expect ( page . getByRole ( "main" )). toBeVisible ();
});
Chat Functionality Test
This test demonstrates how to mock the ADK agent for testing:
import { test , expect } from "@playwright/test" ;
import { MockModel , GenAIAgentService } from "@yagolopez/adk-utils" ;
import { LlmAgent } from "@google/adk" ;
test . describe ( "Chat Functionality" , () => {
test ( "user can send a message and receive a response" , async ({ page }) => {
// Create agent with mock model
const agent = new LlmAgent ({
name: "test_agent" ,
description: "test-description" ,
model: new MockModel ( "mock-model" , 0 , [ "Response from mock model" ]),
instruction: "You are a test agent." ,
});
const service = new GenAIAgentService ( agent );
// Mock the API route
await page . route ( "/api/genai-agent" , async ( route ) => {
const { messages } = route . request (). postDataJSON ();
const response = service . createStreamingResponse ( messages );
const bodyBuffer = await response . arrayBuffer ();
await route . fulfill ({
status: response . status ,
headers: Object . fromEntries ( response . headers . entries ()),
contentType:
response . headers . get ( "content-type" ) || "text/event-stream" ,
body: Buffer . from ( bodyBuffer ),
});
});
await page . goto ( "/" );
const input = page . getByPlaceholder ( "Ask the agent..." );
await input . fill ( "hola" );
const sendButton = page . getByRole ( "button" , { name: "Send message" });
await sendButton . click ();
await expect ( page . getByText ( "Response from mock model" )). toBeVisible ();
});
});
Key Testing Features
Mock Model
Uses MockModel from @yagolopez/adk-utils to create a test agent without calling real AI models.
API Route Mocking
Intercepts API requests with page.route() to test without external dependencies.
User Interaction Simulation
Simulates real user actions like filling input fields and clicking buttons.
Response Validation
Verifies that the mocked response appears correctly in the UI.
Test Dependencies
{
"@playwright/test" : "^1.58.2" ,
"@types/jest" : "^30.0.0" ,
"jest" : "^30.2.0" ,
"ts-jest" : "^29.4.6" ,
"ts-node" : "^10.9.2"
}
CI/CD Integration
The test configuration includes CI-specific optimizations:
{
forbidOnly : !! process . env . CI , // Fail if test.only is found
retries : process . env . CI ? 2 : 0 , // Retry failed tests in CI
workers : process . env . CI ? 3 : undefined , // Parallel workers
}
In CI environments, the production build (npm run start) is used instead of the dev server for more accurate testing.
Best Practices
Unit Testing
Test Pure Functions : Focus on functions with predictable outputs
Mock External Dependencies : Use Jest mocks for API calls and external services
Coverage Goals : Aim for high coverage on critical business logic
E2E Testing
Use Mock Models : Avoid calling real AI APIs in tests for speed and reliability
Test User Flows : Focus on complete user journeys through the application
Parallel Execution : Take advantage of Playwright’s parallel test execution
Trace on Failure : Use trace: "on-first-retry" to debug failing tests
Debugging Tests
Jest Debugging
# Run tests in watch mode with verbose output
npm run test:watch -- --verbose
Playwright Debugging
# Open UI mode for interactive debugging
npm run test:e2e:ui
# Run with headed browser to see what's happening
npm run test:e2e:headed
# Enable debug mode
DEBUG = pw:api npm run test:e2e:headless
Next Steps
Running Locally Set up your development environment
Configuration Configure environment variables and settings