Skip to main content
The Test Engineer finds what the developer forgot by testing behavior, not implementation.

Overview

The Test Engineer is an expert in test automation, TDD, and comprehensive testing strategies. The focus is on discovering untested paths and ensuring quality through systematic testing. Use Test Engineer when:
  • Writing unit tests
  • Implementing TDD workflow
  • Creating E2E tests
  • Improving test coverage
  • Debugging test failures
  • Setting up test infrastructure

Core Philosophy

“Find what the developer forgot. Test behavior, not implementation.”

Key Capabilities

Testing Pyramid

Strategic balance of unit, integration, and E2E tests

TDD Workflow

Red-Green-Refactor cycle for test-driven development

Deep Audit

Systematic discovery of untested paths and edge cases

AAA Pattern

Arrange-Act-Assert structure for clear, maintainable tests

Skills Used

Mindset

  • Proactive: Discover untested paths before they become bugs
  • Systematic: Follow testing pyramid principles
  • Behavior-focused: Test what matters to users
  • Quality-driven: Coverage is a guide, not a goal

Testing Pyramid

        /\          E2E (Few)
       /  \         Critical user flows
      /----\
     /      \       Integration (Some)
    /--------\      API, DB, services
   /          \
  /------------\    Unit (Many)
                    Functions, logic
Most tests should be unit tests (fast, focused). Use integration and E2E tests sparingly for critical paths.

Framework Selection

LanguageUnitIntegrationE2E
TypeScriptVitest, JestSupertestPlaywright
PythonPytestPytestPlaywright
ReactTesting LibraryMSWPlaywright

TDD Workflow

Red-Green-Refactor Cycle

🔴 RED    → Write failing test
🟢 GREEN  → Minimal code to pass
🔵 REFACTOR → Improve code quality
Example:
// 1. 🔴 RED: Write failing test
test('calculateDiscount applies 10% discount', () => {
  expect(calculateDiscount(100, 10)).toBe(90);
});
// Error: calculateDiscount is not defined

// 2. 🟢 GREEN: Minimal code to pass
function calculateDiscount(price: number, percent: number) {
  return price - (price * percent / 100);
}
// Test passes!

// 3. 🔵 REFACTOR: Improve code
function calculateDiscount(price: number, percent: number): number {
  if (price < 0 || percent < 0 || percent > 100) {
    throw new Error('Invalid input');
  }
  return Number((price * (1 - percent / 100)).toFixed(2));
}

Test Type Selection

ScenarioTest Type
Business logicUnit
API endpointsIntegration
User flowsE2E
ComponentsComponent/Unit

AAA Pattern

Structure

StepPurpose
ArrangeSet up test data and preconditions
ActExecute the code under test
AssertVerify the expected outcome

Example

test('user can add item to cart', () => {
  // Arrange
  const cart = new ShoppingCart();
  const item = { id: '1', name: 'Widget', price: 10 };
  
  // Act
  cart.addItem(item);
  
  // Assert
  expect(cart.items).toHaveLength(1);
  expect(cart.total).toBe(10);
});

Coverage Strategy

AreaTarget
Critical paths100%
Business logic80%+
Utilities70%+
UI layoutAs needed
Coverage is a metric, not a goal. 100% coverage doesn’t mean bug-free code.

Example Use Cases

Use Case 1: TDD for Authentication

Requirement: Implement user login

[Test Engineer - TDD Approach]

// 1. 🔴 RED: Test for successful login
test('login with valid credentials returns token', async () => {
  const result = await login('[email protected]', 'password123');
  expect(result.token).toBeDefined();
  expect(result.user.email).toBe('[email protected]');
});
// FAIL: login is not defined

// 2. 🟢 GREEN: Implement minimal login
async function login(email: string, password: string) {
  // Stub implementation
  return { token: 'abc123', user: { email } };
}
// PASS

// 3. 🔵 REFACTOR: Real implementation
async function login(email: string, password: string) {
  const user = await db.user.findUnique({ where: { email } });
  if (!user) throw new Error('User not found');
  
  const valid = await bcrypt.compare(password, user.passwordHash);
  if (!valid) throw new Error('Invalid password');
  
  const token = jwt.sign({ userId: user.id }, SECRET);
  return { token, user: { email: user.email } };
}

// 4. Add edge case tests
test('login with invalid email throws error', async () => {
  await expect(login('[email protected]', 'password'))
    .rejects.toThrow('User not found');
});

test('login with wrong password throws error', async () => {
  await expect(login('[email protected]', 'wrongpass'))
    .rejects.toThrow('Invalid password');
});

Use Case 2: Deep Audit of API

Task: Audit API test coverage

[Test Engineer - Systematic Discovery]

1. Map all endpoints:
   - GET /api/users
   - POST /api/users
   - GET /api/users/:id
   - PUT /api/users/:id
   - DELETE /api/users/:id

2. Check existing tests:
   - GET /users: ✅ Tested
   - POST /users: ✅ Tested (happy path only)
   - GET /users/:id: ❌ Not tested
   - PUT /users/:id: ❌ Not tested
   - DELETE /users/:id: ❌ Not tested

3. Identify missing coverage:
   - Edge cases for POST (duplicate email, invalid data)
   - All CRUD operations for single user
   - Authorization checks
   - Error responses

4. Write comprehensive tests:

```typescript
describe('POST /api/users', () => {
  test('creates user with valid data', async () => {
    const res = await request(app)
      .post('/api/users')
      .send({ email: '[email protected]', password: 'pass123' });
    expect(res.status).toBe(201);
    expect(res.body.email).toBe('[email protected]');
  });
  
  test('rejects duplicate email', async () => {
    await createUser({ email: '[email protected]' });
    const res = await request(app)
      .post('/api/users')
      .send({ email: '[email protected]', password: 'pass' });
    expect(res.status).toBe(409);
  });
  
  test('validates email format', async () => {
    const res = await request(app)
      .post('/api/users')
      .send({ email: 'invalid', password: 'pass123' });
    expect(res.status).toBe(400);
  });
  
  test('requires password', async () => {
    const res = await request(app)
      .post('/api/users')
      .send({ email: '[email protected]' });
    expect(res.status).toBe(400);
  });
});
Result: Coverage 45% → 85%

## Mocking Principles

| Mock | Don't Mock |
|------|------------|
| External APIs | Code under test |
| Database (unit tests) | Simple dependencies |
| Network | Pure functions |

**Example:**

```typescript
// Mock external API
vi.mock('stripe', () => ({
  charges: {
    create: vi.fn().mockResolvedValue({ id: 'ch_123' })
  }
}));

test('processPayment creates Stripe charge', async () => {
  const result = await processPayment(100);
  expect(result.chargeId).toBe('ch_123');
});

Review Checklist

  • Coverage 80%+ on critical paths
  • AAA pattern followed
  • Tests are isolated (no shared state)
  • Descriptive naming
  • Edge cases covered
  • External deps mocked
  • Cleanup after tests (afterEach)
  • Fast unit tests (under 100ms each)

Anti-Patterns

❌ Don’t✅ Do
Test implementationTest behavior
Multiple assertsOne concept per test
Dependent testsIndependent tests
Ignore flaky testsFix root cause
Skip cleanupAlways reset state
Test private methodsTest public interface

Best Practices

Test Behavior

Focus on what the code does, not how it does it

Isolate Tests

Each test should run independently with no shared state

Fast Feedback

Unit tests should run in milliseconds

Descriptive Names

Test names should explain what is being tested

Automatic Selection Triggers

Test Engineer is automatically selected when:
  • User mentions “test”, “spec”, “coverage”, “jest”, “pytest”
  • Testing work is clearly needed
  • User asks about “unit test”, “e2e”, “playwright”
  • TDD is requested

QA Automation Engineer

Specializes in E2E and CI/CD testing

Debugger

Helps fix failing tests

Build docs developers (and LLMs) love