This document outlines the comprehensive testing strategy for MyBrowserControl MCP server, including unit tests, application tests, integration tests, and acceptance tests.
MyBrowserControl follows Test-Driven Design (TDD) principles:
- Write tests first - Tests define expected behavior
- Red-Green-Refactor - Write failing test, make it pass, improve code
- High coverage targets - Aim for 85%+ overall coverage
- Multiple test levels - Unit, application, integration, and acceptance tests
| Test Type | Purpose | Scope | Speed | Dependencies |
|---|---|---|---|---|
| Unit | Test individual components in isolation | Single function/class | Fast (ms) | Mocked |
| Application | Test MCP protocol flow and tool execution | MCP server tools | Medium (100-500ms) | Mocked browser |
| Integration | Test real browser automation | Full stack | Slow (1-5s) | Real browser |
| Acceptance | Test complete user scenarios end-to-end | Full system | Slowest (5-30s) | Real browser + network |
// jest.config.js coverage thresholds
{
unit: {
branches: 95,
functions: 95,
lines: 95,
statements: 95
},
application: {
branches: 90,
functions: 90,
lines: 90,
statements: 90
},
integration: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
},
overall: {
branches: 85,
functions: 85,
lines: 85,
statements: 85
}
}Location: tests/unit/
Current Status: ✅ 46 tests passing
Unit tests verify individual components in complete isolation using mocks:
- index.ts (6 tests) - CLI argument parsing
- server.ts (5 tests) - MCP server initialization
- core/playwright.ts (20 tests) - Browser lifecycle management
- config/types.ts (16 tests) - Configuration types and defaults
# Run all unit tests
npm run test:unit
# Run with coverage
npm run test:unit -- --coverage
# Watch mode for TDD
npm run test:watch -- --selectProjects unit
# Run specific test file
npm run test:unit -- tests/unit/server.test.ts// tests/unit/core/playwright.test.ts
describe('PlaywrightCore', () => {
it('should launch chromium browser by default', async () => {
const { chromium } = require('playwright');
chromium.launch.mockResolvedValue(mockBrowser);
await playwright.launch();
expect(chromium.launch).toHaveBeenCalledWith({
headless: true,
timeout: 30000,
});
expect(playwright.isRunning()).toBe(true);
});
});- Create test file:
tests/unit/<module>/<file>.test.ts - Import module to test
- Mock all dependencies using
jest.mock() - Write focused tests for each function/method
- Assert behavior, not implementation
Location: tests/application/
Status: 🚧 To be implemented
Application tests verify MCP protocol interactions and tool execution without real browsers:
- MCP tool listing (
ListToolsRequest) - MCP tool execution (
CallToolRequest) - Request/response format validation
- Error handling and edge cases
- Tool schema validation
// tests/application/mcp-tools.test.ts
describe('MCP Tool Protocol', () => {
it('should list all available tools', async () => {
const server = new MyBrowserControlServer();
const tools = await server.listTools();
expect(tools).toHaveLength(4);
expect(tools.map(t => t.name)).toEqual([
'browser_navigate',
'browser_screenshot',
'browser_content',
'browser_close'
]);
});
it('should execute browser_navigate tool', async () => {
const server = new MyBrowserControlServer();
const result = await server.callTool('browser_navigate', {
url: 'https://example.com'
});
expect(result.content[0].text).toContain('Navigated');
});
});# Run all application tests (when implemented)
npm run test:app
# Watch mode
npm run test:watch -- --selectProjects applicationLocation: tests/integration/
Status: 🚧 To be implemented
Integration tests use real Playwright browsers to verify actual browser automation:
- Real browser launching (Chromium, Firefox, WebKit)
- Actual page navigation
- Real screenshot capture
- Actual HTML content retrieval
- Browser lifecycle management
// tests/integration/browser-automation.test.ts
describe('Browser Automation Integration', () => {
let playwright: PlaywrightCore;
beforeEach(() => {
playwright = new PlaywrightCore({
mode: BrowserMode.NATIVE,
headless: true,
timeout: 30000
});
});
afterEach(async () => {
await playwright.close();
});
it('should navigate to real website', async () => {
await playwright.launch();
await playwright.navigate('https://example.com');
const content = await playwright.getContent();
expect(content).toContain('Example Domain');
});
it('should capture real screenshot', async () => {
await playwright.launch();
await playwright.navigate('https://example.com');
const screenshot = await playwright.screenshot();
expect(screenshot).toBeInstanceOf(Buffer);
expect(screenshot.length).toBeGreaterThan(1000);
});
});# Run all integration tests (when implemented)
npm run test:integration
# Run with specific browser
npm run test:integration -- --browser=firefox- Use real browsers - No mocks for browser objects
- Test simple pages first - Use example.com, httpbin.org
- Longer timeouts - Browser operations are slow (5-30 seconds)
- Cleanup after each test - Always close browsers
- Skip in CI if needed - Can be slow/flaky
Location: tests/acceptance/
Status: 🚧 To be implemented
Acceptance tests verify complete user scenarios end-to-end:
- Complete MCP protocol flow from client to browser
- Real-world user scenarios
- Multi-step tool sequences
- Error recovery
- Performance benchmarks
// tests/acceptance/user-scenarios.test.ts
describe('User Scenarios', () => {
it('should complete full browsing workflow', async () => {
// 1. Start server
const server = new MyBrowserControlServer();
await server.start();
// 2. Navigate to page
const navResult = await server.callTool('browser_navigate', {
url: 'https://example.com'
});
expect(navResult.content[0].text).toContain('Navigated');
// 3. Capture screenshot
const screenshot = await server.callTool('browser_screenshot', {
fullPage: false
});
expect(screenshot.content[0].type).toBe('image');
// 4. Get content
const content = await server.callTool('browser_content', {});
expect(content.content[0].text).toContain('<!DOCTYPE');
// 5. Close browser
const close = await server.callTool('browser_close', {});
expect(close.content[0].text).toContain('closed');
});
it('should handle error recovery', async () => {
const server = new MyBrowserControlServer();
await server.start();
// Try to screenshot without navigating - should fail gracefully
await expect(
server.callTool('browser_screenshot', {})
).rejects.toThrow('Browser not launched');
// Should still be able to navigate after error
const result = await server.callTool('browser_navigate', {
url: 'https://example.com'
});
expect(result.content[0].text).toContain('Navigated');
});
});# Run all acceptance tests (when implemented)
npm run test:acceptance
# Run with verbose output
npm run test:acceptance -- --verboseIn addition to automated tests, use the MCP Inspector for manual testing:
# Launch Inspector
npx @modelcontextprotocol/inspector node dist/index.jsSee INSPECTOR.md for detailed Inspector usage guide.
-
Write failing test first:
npm run test:watch -- --selectProjects unit
-
Implement feature to make test pass
-
Refactor while keeping tests green
-
Add integration test to verify with real dependencies
-
Test with Inspector for manual validation
-
Add acceptance test for complete scenario
// 1. Write unit test (RED)
it('should execute new_tool', async () => {
const result = await server.callTool('new_tool', { param: 'value' });
expect(result).toBeDefined();
});
// 2. Implement tool (GREEN)
// ... add tool to server.ts ...
// 3. Add application test
it('should validate new_tool schema', async () => {
const tools = await server.listTools();
const newTool = tools.find(t => t.name === 'new_tool');
expect(newTool.inputSchema).toBeDefined();
});
// 4. Add integration test
it('should execute new_tool with real browser', async () => {
// ... test with real Playwright ...
});
// 5. Test with Inspector
// Manual verification in browser UI
// 6. Add acceptance test
it('should use new_tool in complete workflow', async () => {
// ... end-to-end scenario ...
});# Before every commit
npm run build # Ensure TypeScript compiles
npm run lint # Check code style
npm run test:unit # Fast unit tests must pass# .github/workflows/test.yml (future)
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm install
- run: npm run build
- run: npm run lint
- run: npm run test:unit
- run: npm run test:app
- run: npm run test:integration
- run: npm run test:all -- --coverage
- run: npx playwright install chromium# Run specific test file
npm test -- tests/unit/server.test.ts
# Run specific test by name
npm test -- -t "should create instance"
# Run with debugging
node --inspect-brk node_modules/.bin/jest --runInBandIssue: Tests timeout
- Increase jest timeout:
jest.setTimeout(30000) - Check for unresolved promises
- Ensure proper cleanup in
afterEach
Issue: Tests pass individually but fail together
- Shared state between tests
- Missing cleanup in
afterEach - Test pollution - use
jest.resetModules()
Issue: Flaky integration tests
- Network issues - use local test servers
- Timing issues - add proper waits
- Browser not ready - increase timeouts
# Generate HTML coverage report
npm run test:all -- --coverage
# Open coverage report
open coverage/index.html # macOS
xdg-open coverage/index.html # Linux- Statements: % of executable statements run
- Branches: % of if/else branches tested
- Functions: % of functions called
- Lines: % of lines executed
- Identify uncovered lines in report
- Add tests for missing paths
- Test error conditions
- Test edge cases
- Remove dead code
// tests/performance/benchmarks.test.ts
it('should navigate within 3 seconds', async () => {
const start = Date.now();
await playwright.navigate('https://example.com');
const duration = Date.now() - start;
expect(duration).toBeLessThan(3000);
});
it('should handle 10 screenshots in < 30s', async () => {
const start = Date.now();
for (let i = 0; i < 10; i++) {
await playwright.screenshot();
}
const duration = Date.now() - start;
expect(duration).toBeLessThan(30000);
});✅ Write tests before code (TDD)
✅ Keep tests focused and small
✅ Use descriptive test names
✅ Mock external dependencies
✅ Test error cases
✅ Clean up resources in afterEach
✅ Use appropriate test type for scenario
✅ Maintain high coverage
❌ Test implementation details
❌ Share state between tests
❌ Use real network calls in unit tests
❌ Skip cleanup
❌ Ignore flaky tests
❌ Test multiple things in one test
❌ Use any type in tests
❌ Commit failing tests
- Implement application tests (MCP protocol)
- Implement integration tests (real browsers)
- Implement acceptance tests (end-to-end)
- Add visual regression testing
- Add performance benchmarks
- Set up CI/CD pipeline
- Add mutation testing
- Generate test documentation
- Add security testing
- Add accessibility testing
Unit Tests: 46 passing ✅
Application Tests: 0 (pending) 🚧
Integration Tests: 0 (pending) 🚧
Acceptance Tests: 0 (pending) 🚧
Total Coverage: ~60% (unit only)
Target Coverage: 85% (all tests)
Updated: 2025-01-07