Back to Home
Kits & Agents

Tester Agent: Complete Guide

Automated test generation and coverage analysis with the Tester agent for comprehensive quality assurance

Overview

The Tester agent is GemKit's quality assurance specialist, designed to generate comprehensive test suites, analyze coverage gaps, and ensure your code meets high quality standards. Unlike generic AI that might write a few basic tests, the Tester agent understands testing methodologies, knows multiple testing frameworks, and can create unit tests, integration tests, E2E tests, and edge case scenarios that developers often overlook.

Core Capabilities:

  • Unit test generation - Vitest, Jest, testing-library
  • Integration test scaffolding - API endpoints, database operations
  • E2E test scenarios - Playwright, Cypress
  • Coverage gap identification - Analyzes and fills missing test cases
  • Edge case testing - Error conditions, boundary values, race conditions

When to Use Tester

✅ Ideal Use Cases

Test Generation for Existing Code

  • Legacy code without tests
  • Newly implemented features
  • Refactored components
  • Third-party code you've adopted

Improving Test Coverage

  • Achieving target coverage goals (80%, 90%, etc.)
  • Filling gaps in existing test suites
  • Adding edge case tests
  • Testing error handling paths

E2E Test Creation

  • User flow testing with Playwright
  • Critical path verification
  • Regression test suites
  • Cross-browser compatibility tests

Testing Edge Cases

  • Boundary value testing
  • Error condition handling
  • Race condition scenarios
  • Network failure simulation

❌ Not Ideal For

Implementation Work → Use Code-Executor agent

  • Writing production code
  • Implementing features
  • Refactoring logic

Research Tasks → Use Researcher agent

  • Finding best testing practices
  • Comparing testing frameworks
  • Investigating testing strategies

Spawning the Tester Agent

Basic Usage

bash
gk agent spawn --agent tester \
  --context src/services/payment.ts \
  --prompt "Write comprehensive Vitest tests for this payment service"

What happens:

  1. Agent analyzes the source code
  2. Identifies functions, classes, and logic paths
  3. Determines appropriate test cases
  4. Generates test file with multiple scenarios
  5. Includes edge cases and error handling tests
  6. Provides coverage analysis

Generate Unit Tests

bash
# Single file testing
gk agent spawn --agent tester \
  --context src/utils/validation.ts \
  --prompt "Generate Vitest unit tests covering all validation functions"

# Multiple related files
gk agent spawn --agent tester \
  --context src/services/auth.ts,src/utils/jwt.ts \
  --prompt "Write unit tests for authentication service and JWT utilities"

# With specific framework
gk agent spawn --agent tester \
  --context src/components/Button.tsx \
  --prompt "Write Jest tests with React Testing Library for this Button component"

Integration Tests

bash
# API endpoint testing
gk agent spawn --agent tester \
  --skills "testing,backend-development" \
  --context src/api/users.ts,src/middleware/ \
  --prompt "Create integration tests for user API endpoints. Test authentication, validation, and database operations."

# Database integration
gk agent spawn --agent tester \
  --skills "testing,backend-development" \
  --context src/models/User.ts,src/repositories/userRepository.ts \
  --prompt "Write integration tests for User model and repository. Use in-memory SQLite for testing."

E2E Tests with Playwright

bash
# User flow testing
gk agent spawn --agent tester \
  --skills "testing,frontend-development" \
  --context src/pages/checkout/ \
  --prompt "Write Playwright E2E tests for the complete checkout flow: cart → shipping → payment → confirmation"

# Multiple scenarios
gk agent spawn --agent tester \
  --skills "testing" \
  --prompt "Create Playwright test suite for user authentication:
  - Successful login
  - Invalid credentials
  - Password reset flow
  - Session persistence
  - Logout"

Coverage Analysis and Gap Filling

bash
# Analyze coverage report
gk agent spawn --agent tester \
  --context coverage/lcov-report/index.html,src/services/ \
  --prompt "Analyze the coverage report and generate tests for uncovered branches in the services directory"

# Target specific coverage goal
gk agent spawn --agent tester \
  --context src/utils/,tests/utils/ \
  --prompt "Current coverage is 67%. Generate additional tests to reach 90% coverage for the utils directory."

Real-World Scenarios

Scenario 1: Achieving 100% Coverage

Context: You have a utility module with partial test coverage.

bash
# Step 1: Check current coverage
npm run test:coverage

# Output shows:
# src/utils/currency.ts: 72% coverage
# Uncovered lines: 15-18, 34-38, 52

# Step 2: Spawn Tester agent
gk agent spawn --agent tester \
  --context src/utils/currency.ts,tests/utils/currency.test.ts \
  --prompt "Current coverage is 72%. Analyze uncovered lines (15-18, 34-38, 52) and add tests to reach 100% coverage. Include edge cases."

Expected output:

typescript
// tests/utils/currency.test.ts (additions)

describe('currency edge cases', () => {
  // Lines 15-18: Error handling for invalid currencies
  it('should throw error for invalid currency code', () => {
    expect(() => formatCurrency(100, 'INVALID')).toThrow('Unknown currency code');
  });

  // Lines 34-38: Negative number handling
  it('should format negative amounts correctly', () => {
    expect(formatCurrency(-100, 'USD')).toBe('-$100.00');
  });

  it('should handle very large negative numbers', () => {
    expect(formatCurrency(-999999999.99, 'USD')).toBe('-$999,999,999.99');
  });

  // Line 52: Zero value edge case
  it('should format zero correctly', () => {
    expect(formatCurrency(0, 'USD')).toBe('$0.00');
  });

  // Additional edge cases
  it('should handle floating point precision', () => {
    expect(formatCurrency(0.1 + 0.2, 'USD')).toBe('$0.30');
  });

  it('should handle very small fractional amounts', () => {
    expect(formatCurrency(0.001, 'USD')).toBe('$0.00');
  });
});

Result: Coverage increases from 72% → 100%

Scenario 2: Testing React Components

Context: New React component needs comprehensive tests.

bash
gk agent spawn --agent tester \
  --skills "testing,frontend-development" \
  --context src/components/UserProfile.tsx,src/hooks/useAuth.ts \
  --prompt "Write comprehensive React Testing Library tests for UserProfile component. Include:
  - Rendering with different user states
  - Loading and error states
  - User interaction tests (edit button, save)
  - Accessibility tests
  - Mock API calls"

Expected output:

typescript
// tests/components/UserProfile.test.tsx

import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { UserProfile } from '@/components/UserProfile';
import { useAuth } from '@/hooks/useAuth';

// Mock hooks
vi.mock('@/hooks/useAuth');

describe('UserProfile', () => {
  const mockUser = {
    id: '1',
    name: 'John Doe',
    email: 'john@example.com',
    avatar: 'https://example.com/avatar.jpg'
  };

  beforeEach(() => {
    vi.mocked(useAuth).mockReturnValue({
      user: mockUser,
      loading: false,
      error: null
    });
  });

  describe('Rendering', () => {
    it('should render user information correctly', () => {
      render(<UserProfile />);

      expect(screen.getByText('John Doe')).toBeInTheDocument();
      expect(screen.getByText('john@example.com')).toBeInTheDocument();
      expect(screen.getByRole('img', { name: /avatar/i })).toHaveAttribute(
        'src',
        mockUser.avatar
      );
    });

    it('should render loading state', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: null,
        loading: true,
        error: null
      });

      render(<UserProfile />);
      expect(screen.getByRole('progressbar')).toBeInTheDocument();
    });

    it('should render error state', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: null,
        loading: false,
        error: new Error('Failed to load user')
      });

      render(<UserProfile />);
      expect(screen.getByText(/failed to load user/i)).toBeInTheDocument();
    });
  });

  describe('User Interactions', () => {
    it('should open edit mode when edit button is clicked', () => {
      render(<UserProfile />);

      const editButton = screen.getByRole('button', { name: /edit/i });
      fireEvent.click(editButton);

      expect(screen.getByRole('textbox', { name: /name/i })).toHaveValue('John Doe');
      expect(screen.getByRole('button', { name: /save/i })).toBeInTheDocument();
    });

    it('should save changes when save button is clicked', async () => {
      const mockUpdateUser = vi.fn().mockResolvedValue({});
      vi.mocked(useAuth).mockReturnValue({
        user: mockUser,
        loading: false,
        error: null,
        updateUser: mockUpdateUser
      });

      render(<UserProfile />);

      // Enter edit mode
      fireEvent.click(screen.getByRole('button', { name: /edit/i }));

      // Change name
      const nameInput = screen.getByRole('textbox', { name: /name/i });
      fireEvent.change(nameInput, { target: { value: 'Jane Doe' } });

      // Save
      fireEvent.click(screen.getByRole('button', { name: /save/i }));

      await waitFor(() => {
        expect(mockUpdateUser).toHaveBeenCalledWith({
          ...mockUser,
          name: 'Jane Doe'
        });
      });
    });

    it('should cancel edit mode', () => {
      render(<UserProfile />);

      // Enter edit mode
      fireEvent.click(screen.getByRole('button', { name: /edit/i }));
      expect(screen.getByRole('textbox', { name: /name/i })).toBeInTheDocument();

      // Cancel
      fireEvent.click(screen.getByRole('button', { name: /cancel/i }));
      expect(screen.queryByRole('textbox', { name: /name/i })).not.toBeInTheDocument();
    });
  });

  describe('Accessibility', () => {
    it('should have proper ARIA labels', () => {
      render(<UserProfile />);

      expect(screen.getByRole('img', { name: /avatar/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /edit profile/i })).toBeInTheDocument();
    });

    it('should be keyboard navigable', () => {
      render(<UserProfile />);

      const editButton = screen.getByRole('button', { name: /edit/i });
      editButton.focus();
      expect(document.activeElement).toBe(editButton);
    });
  });

  describe('Edge Cases', () => {
    it('should handle missing avatar gracefully', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: { ...mockUser, avatar: null },
        loading: false,
        error: null
      });

      render(<UserProfile />);
      expect(screen.getByText('JD')).toBeInTheDocument(); // Initials fallback
    });

    it('should handle very long names', () => {
      vi.mocked(useAuth).mockReturnValue({
        user: { ...mockUser, name: 'A'.repeat(100) },
        loading: false,
        error: null
      });

      render(<UserProfile />);
      const nameElement = screen.getByText(/A{100}/);
      expect(nameElement).toHaveStyle({ textOverflow: 'ellipsis' });
    });
  });
});

Coverage: Component 96%, hooks tested via integration

Scenario 3: API Contract Testing

Context: REST API endpoints need integration tests.

bash
gk agent spawn --agent tester \
  --skills "testing,backend-development" \
  --context src/api/products.ts,src/models/Product.ts \
  --prompt "Create integration tests for products API using supertest and MSW for database mocking. Test:
  - GET /api/products (list with pagination)
  - GET /api/products/:id (single product)
  - POST /api/products (create)
  - PUT /api/products/:id (update)
  - DELETE /api/products/:id (delete)
  Include authentication, validation errors, and edge cases."

Expected output:

typescript
// tests/integration/products.test.ts

import request from 'supertest';
import { app } from '@/app';
import { db } from '@/config/database';
import { setupTestDatabase, teardownTestDatabase } from '@/test-utils/database';

describe('Products API', () => {
  beforeAll(async () => {
    await setupTestDatabase();
  });

  afterAll(async () => {
    await teardownTestDatabase();
  });

  beforeEach(async () => {
    // Clear products table
    await db.products.deleteMany();
  });

  describe('GET /api/products', () => {
    it('should return empty array when no products exist', async () => {
      const response = await request(app)
        .get('/api/products')
        .expect(200);

      expect(response.body).toEqual({
        data: [],
        pagination: { page: 1, limit: 10, total: 0 }
      });
    });

    it('should return paginated products', async () => {
      // Create 25 products
      await db.products.createMany({
        data: Array.from({ length: 25 }, (_, i) => ({
          name: `Product ${i + 1}`,
          price: 100 + i
        }))
      });

      const response = await request(app)
        .get('/api/products?page=2&limit=10')
        .expect(200);

      expect(response.body.data).toHaveLength(10);
      expect(response.body.pagination).toEqual({
        page: 2,
        limit: 10,
        total: 25
      });
    });

    it('should filter products by price range', async () => {
      await db.products.createMany({
        data: [
          { name: 'Cheap', price: 10 },
          { name: 'Medium', price: 50 },
          { name: 'Expensive', price: 100 }
        ]
      });

      const response = await request(app)
        .get('/api/products?minPrice=20&maxPrice=80')
        .expect(200);

      expect(response.body.data).toHaveLength(1);
      expect(response.body.data[0].name).toBe('Medium');
    });
  });

  describe('GET /api/products/:id', () => {
    it('should return product by ID', async () => {
      const product = await db.products.create({
        data: { name: 'Test Product', price: 99.99 }
      });

      const response = await request(app)
        .get(`/api/products/${product.id}`)
        .expect(200);

      expect(response.body).toMatchObject({
        id: product.id,
        name: 'Test Product',
        price: 99.99
      });
    });

    it('should return 404 for non-existent product', async () => {
      await request(app)
        .get('/api/products/non-existent-id')
        .expect(404);
    });
  });

  describe('POST /api/products', () => {
    it('should create new product with valid data', async () => {
      const newProduct = {
        name: 'New Product',
        price: 49.99,
        description: 'A test product'
      };

      const response = await request(app)
        .post('/api/products')
        .set('Authorization', 'Bearer valid-token')
        .send(newProduct)
        .expect(201);

      expect(response.body).toMatchObject(newProduct);
      expect(response.body.id).toBeDefined();
    });

    it('should require authentication', async () => {
      await request(app)
        .post('/api/products')
        .send({ name: 'Product', price: 10 })
        .expect(401);
    });

    it('should validate required fields', async () => {
      const response = await request(app)
        .post('/api/products')
        .set('Authorization', 'Bearer valid-token')
        .send({ price: 10 }) // Missing name
        .expect(400);

      expect(response.body.errors).toContainEqual(
        expect.objectContaining({ field: 'name' })
      );
    });

    it('should validate price is positive', async () => {
      const response = await request(app)
        .post('/api/products')
        .set('Authorization', 'Bearer valid-token')
        .send({ name: 'Product', price: -10 })
        .expect(400);

      expect(response.body.errors).toContainEqual(
        expect.objectContaining({
          field: 'price',
          message: expect.stringContaining('positive')
        })
      );
    });
  });

  describe('PUT /api/products/:id', () => {
    it('should update existing product', async () => {
      const product = await db.products.create({
        data: { name: 'Original', price: 100 }
      });

      const response = await request(app)
        .put(`/api/products/${product.id}`)
        .set('Authorization', 'Bearer valid-token')
        .send({ name: 'Updated', price: 150 })
        .expect(200);

      expect(response.body).toMatchObject({
        id: product.id,
        name: 'Updated',
        price: 150
      });
    });

    it('should return 404 for non-existent product', async () => {
      await request(app)
        .put('/api/products/non-existent-id')
        .set('Authorization', 'Bearer valid-token')
        .send({ name: 'Updated' })
        .expect(404);
    });
  });

  describe('DELETE /api/products/:id', () => {
    it('should delete existing product', async () => {
      const product = await db.products.create({
        data: { name: 'To Delete', price: 100 }
      });

      await request(app)
        .delete(`/api/products/${product.id}`)
        .set('Authorization', 'Bearer valid-token')
        .expect(204);

      // Verify deletion
      const deleted = await db.products.findUnique({
        where: { id: product.id }
      });
      expect(deleted).toBeNull();
    });

    it('should require authentication', async () => {
      await request(app)
        .delete('/api/products/some-id')
        .expect(401);
    });
  });
});

Coverage: API endpoints 100%, authentication middleware tested

Scenario 4: E2E User Flow Testing

Context: Critical checkout flow needs E2E tests.

bash
gk agent spawn --agent tester \
  --skills "testing,frontend-development" \
  --prompt "Write Playwright E2E tests for complete e-commerce checkout flow:
  1. Browse products
  2. Add items to cart
  3. Proceed to checkout
  4. Fill shipping information
  5. Enter payment details
  6. Place order
  7. Verify order confirmation
  Include error scenarios like payment failure."

Expected output:

typescript
// tests/e2e/checkout.spec.ts

import { test, expect } from '@playwright/test';

test.describe('Checkout Flow', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/');
  });

  test('complete successful checkout', async ({ page }) => {
    // 1. Browse products
    await page.goto('/products');
    await expect(page.locator('h1')).toContainText('Products');

    // 2. Add items to cart
    await page.locator('[data-testid="product-1"]').click();
    await page.locator('button', { hasText: 'Add to Cart' }).click();
    await expect(page.locator('[data-testid="cart-count"]')).toHaveText('1');

    // Add another item
    await page.goto('/products');
    await page.locator('[data-testid="product-2"]').click();
    await page.locator('button', { hasText: 'Add to Cart' }).click();
    await expect(page.locator('[data-testid="cart-count"]')).toHaveText('2');

    // 3. Proceed to checkout
    await page.locator('[data-testid="cart-icon"]').click();
    await page.locator('button', { hasText: 'Checkout' }).click();

    // 4. Fill shipping information
    await expect(page).toHaveURL(/.*\/checkout\/shipping/);
    await page.fill('[name="firstName"]', 'John');
    await page.fill('[name="lastName"]', 'Doe');
    await page.fill('[name="address"]', '123 Main St');
    await page.fill('[name="city"]', 'San Francisco');
    await page.fill('[name="zipCode"]', '94102');
    await page.selectOption('[name="country"]', 'US');
    await page.click('button', { hasText: 'Continue to Payment' });

    // 5. Enter payment details
    await expect(page).toHaveURL(/.*\/checkout\/payment/);
    await page.fill('[name="cardNumber"]', '4242424242424242');
    await page.fill('[name="cardExpiry"]', '12/25');
    await page.fill('[name="cardCvc"]', '123');
    await page.fill('[name="cardName"]', 'John Doe');

    // 6. Place order
    await page.click('button', { hasText: 'Place Order' });

    // 7. Verify order confirmation
    await expect(page).toHaveURL(/.*\/order\/confirmation/);
    await expect(page.locator('h1')).toContainText('Order Confirmed');
    await expect(page.locator('[data-testid="order-number"]')).toBeVisible();

    // Verify order details
    await expect(page.locator('[data-testid="order-items"]')).toContainText('Product 1');
    await expect(page.locator('[data-testid="order-items"]')).toContainText('Product 2');
    await expect(page.locator('[data-testid="shipping-address"]')).toContainText('123 Main St');
  });

  test('handle payment failure', async ({ page }) => {
    // Add item and go to payment
    await page.goto('/products');
    await page.locator('[data-testid="product-1"]').click();
    await page.locator('button', { hasText: 'Add to Cart' }).click();
    await page.locator('[data-testid="cart-icon"]').click();
    await page.locator('button', { hasText: 'Checkout' }).click();

    // Fill shipping
    await page.fill('[name="firstName"]', 'John');
    await page.fill('[name="lastName"]', 'Doe');
    await page.fill('[name="address"]', '123 Main St');
    await page.fill('[name="city"]', 'San Francisco');
    await page.fill('[name="zipCode"]', '94102');
    await page.selectOption('[name="country"]', 'US');
    await page.click('button', { hasText: 'Continue to Payment' });

    // Use test card that fails
    await page.fill('[name="cardNumber"]', '4000000000000002');
    await page.fill('[name="cardExpiry"]', '12/25');
    await page.fill('[name="cardCvc"]', '123');
    await page.fill('[name="cardName"]', 'John Doe');
    await page.click('button', { hasText: 'Place Order' });

    // Verify error message
    await expect(page.locator('[role="alert"]')).toContainText('Payment failed');
    await expect(page).toHaveURL(/.*\/checkout\/payment/);

    // Verify can retry
    await expect(page.locator('button', { hasText: 'Place Order' })).toBeEnabled();
  });

  test('validate shipping form', async ({ page }) => {
    await page.goto('/products');
    await page.locator('[data-testid="product-1"]').click();
    await page.locator('button', { hasText: 'Add to Cart' }).click();
    await page.locator('[data-testid="cart-icon"]').click();
    await page.locator('button', { hasText: 'Checkout' }).click();

    // Try to continue without filling form
    await page.click('button', { hasText: 'Continue to Payment' });

    // Verify validation errors
    await expect(page.locator('[data-error="firstName"]')).toBeVisible();
    await expect(page.locator('[data-error="address"]')).toBeVisible();

    // Still on shipping page
    await expect(page).toHaveURL(/.*\/checkout\/shipping/);
  });

  test('maintain cart contents across page refresh', async ({ page }) => {
    // Add items
    await page.goto('/products');
    await page.locator('[data-testid="product-1"]').click();
    await page.locator('button', { hasText: 'Add to Cart' }).click();

    // Refresh page
    await page.reload();

    // Verify cart persists
    await expect(page.locator('[data-testid="cart-count"]')).toHaveText('1');
  });
});

Coverage: Complete user journey, error scenarios, validation

Testing Skills Reference

The Tester agent leverages these skills for domain-specific testing knowledge:

testing skill - General testing methodology

  • Test structure and organization
  • Mocking and stubbing patterns
  • Coverage analysis
  • Edge case identification

vitest skill (if available) - Vitest-specific patterns

  • Vitest configuration
  • vi.mock() usage
  • Snapshot testing
  • Performance testing

jest skill (if available) - Jest-specific patterns

  • Jest matchers
  • jest.mock() patterns
  • Timer mocking
  • Module mocking

playwright skill (if available) - E2E testing

  • Page object model
  • Locator strategies
  • Visual regression testing
  • Network interception

Best Practices

1. Provide the Source Code as Context

bash
# ❌ No context
gk agent spawn --agent tester --prompt "Write tests for auth service"

# ✅ With context
gk agent spawn --agent tester \
  --context src/services/auth.ts \
  --prompt "Write tests for auth service"

2. Specify Testing Framework Preference

bash
# ✅ Be explicit
gk agent spawn --agent tester \
  --context src/utils/currency.ts \
  --prompt "Write Vitest tests using @testing-library/react"

3. Ask for Edge Cases Explicitly

bash
# ✅ Request comprehensive coverage
gk agent spawn --agent tester \
  --context src/services/payment.ts \
  --prompt "Write tests including edge cases:
  - Network failures
  - Invalid card numbers
  - Duplicate transactions
  - Race conditions"

4. Request Coverage Report Analysis

bash
# ✅ Use coverage data
gk agent spawn --agent tester \
  --context src/utils/,coverage/lcov.info \
  --prompt "Analyze coverage report and add tests for uncovered branches"

5. Combine with Code-Executor for TDD

bash
# Write tests first
gk agent spawn --agent tester \
  --prompt "Write Vitest tests for a currency converter function supporting USD, EUR, GBP"

# Then implement
gk agent spawn --agent code-executor \
  --context tests/utils/currency.test.ts \
  --prompt "Implement currency converter to pass these tests"

Summary

The Tester agent is your quality assurance partner, specializing in comprehensive test generation across unit, integration, and E2E testing. By understanding testing frameworks, methodologies, and edge cases, it helps you achieve high coverage and confidence in your code quality. Use it to fill testing gaps, achieve coverage goals, and ensure robust error handling.

Key takeaways:

  • Always provide source code as context for accurate tests
  • Specify testing framework and tools explicitly
  • Request edge cases and error scenarios
  • Use coverage reports to identify gaps
  • Combine with Code-Executor for test-driven development
  • Review generated tests to ensure they match your standards
Caught a mistake? Edit this page on GitHub
Updated: Jan 20, 2026