---
description: 
globs: 
alwaysApply: false
---
# Testing Guidelines

## Test Structure (AAA Pattern)
```typescript
describe('UserService', () => {
  describe('when getting user by ID', () => {
    it('should return user data for valid ID', async () => {
      // Arrange
      const userId = '123';
      const expectedUser = { id: '123', name: 'John' };
      mockUserRepository.findById.mockResolvedValue(expectedUser);

      // Act
      const result = await userService.getUserById(userId);

      // Assert
      expect(result).toEqual(expectedUser);
    });
  });
});
```

## React Component Testing
```typescript
// ✅ Test user behavior, not implementation
describe('LoginForm', () => {
  it('should display error message for invalid credentials', async () => {
    const mockOnSubmit = jest.fn().mockRejectedValue(new Error('Invalid credentials'));
    render(<LoginForm onSubmit={mockOnSubmit} />);

    await user.type(screen.getByLabelText(/email/i), 'invalid@example.com');
    await user.type(screen.getByLabelText(/password/i), 'wrongpassword');
    await user.click(screen.getByRole('button', { name: /sign in/i }));

    expect(await screen.findByText(/invalid credentials/i)).toBeInTheDocument();
  });
});
```

## Mocking Patterns
```typescript
// ✅ Service mocking
const mockEmailService = {
  sendEmail: jest.fn().mockResolvedValue({ success: true }),
  validateEmail: jest.fn().mockReturnValue(true),
};

// ✅ Test data factories
const createTestUser = (overrides = {}) => ({
  id: uuid(),
  email: 'test@example.com',
  name: 'Test User',
  ...overrides,
});

beforeEach(() => {
  jest.clearAllMocks();
});
```

## Testing Principles
- **Test behavior, not implementation** - Focus on what users see/do
- **Use descriptive test names** - "should [behavior] when [condition]"
- **Query by user-visible elements** - text, roles, labels over test IDs
- **Keep tests isolated** - Independent and repeatable
- **70% unit, 20% integration, 10% E2E** - Test pyramid

## Common Patterns
```typescript
// Async testing
await waitFor(() => {
  expect(screen.getByText('Loading...')).not.toBeInTheDocument();
});

// User interactions
await user.click(screen.getByRole('button'));
await user.type(screen.getByLabelText(/search/i), 'query');

// API integration tests
const response = await request(app)
  .post('/api/users')
  .send(userData)
  .expect(201);
```
