# QA Best Practices for React and Next.js
Guidelines for writing maintainable tests, debugging failures, and measuring test quality.
---
## Table of Contents
- [Writing Testable Code](#writing-testable-code)
- [Test Naming Conventions](#test-naming-conventions)
- [Arrange-Act-Assert Pattern](#arrange-act-assert-pattern)
- [Test Isolation Principles](#test-isolation-principles)
- [Handling Flaky Tests](#handling-flaky-tests)
- [Code Review for Testability](#code-review-for-testability)
- [Test Maintenance Strategies](#test-maintenance-strategies)
- [Debugging Failed Tests](#debugging-failed-tests)
- [Quality Metrics and KPIs](#quality-metrics-and-kpis)
---
## Writing Testable Code
Testable code is easy to understand, has clear boundaries, and minimizes dependencies.
### Dependency Injection
Instead of creating dependencies inside functions, pass them as parameters.
**Hard to Test:**
```typescript
// src/services/userService.ts
import { prisma } from '../lib/prisma';
import { sendEmail } from '../lib/email';
export async function createUser(data: UserInput) {
const user = await prisma.user.create({ data });
await sendEmail(user.email, 'Welcome!');
return user;
}
```
**Easy to Test:**
```typescript
// src/services/userService.ts
export function createUserService(
db: PrismaClient,
emailService: EmailService
) {
return {
async createUser(data: UserInput) {
const user = await db.user.create({ data });
await emailService.send(user.email, 'Welcome!');
return user;
},
};
}
// Usage in app
const userService = createUserService(prisma, emailService);
// Usage in tests
const mockDb = { user: { create: jest.fn() } };
const mockEmail = { send: jest.fn() };
const testService = createUserService(mockDb, mockEmail);
```
### Pure Functions
Pure functions are deterministic and have no side effects, making them trivial to test.
**Impure (Hard to Test):**
```typescript
function formatTimestamp() {
const now = new Date();
return `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`;
}
```
**Pure (Easy to Test):**
```typescript
function formatTimestamp(date: Date): string {
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
}
// Test
expect(formatTimestamp(new Date('2024-03-15'))).toBe('2024-3-15');
```
### Separation of Concerns
Separate business logic from UI and I/O operations.
**Mixed Concerns (Hard to Test):**
```typescript
// Component with embedded business logic
function CheckoutForm() {
const [total, setTotal] = useState(0);
const handleSubmit = async (items: CartItem[]) => {
// Business logic mixed with UI
let sum = 0;
for (const item of items) {
sum += item.price * item.quantity;
if (item.category === 'electronics') {
sum *= 0.9; // 10% discount
}
}
const tax = sum * 0.08;
const finalTotal = sum + tax;
// API call
await fetch('/api/orders', {
method: 'POST',
body: JSON.stringify({ items, total: finalTotal }),
});
setTotal(finalTotal);
};
return
;
}
```
**Separated Concerns (Easy to Test):**
```typescript
// Pure business logic (easy to unit test)
export function calculateOrderTotal(items: CartItem[]): number {
return items.reduce((sum, item) => {
const subtotal = item.price * item.quantity;
const discount = item.category === 'electronics' ? 0.9 : 1;
return sum + subtotal * discount;
}, 0);
}
export function calculateTax(subtotal: number, rate = 0.08): number {
return subtotal * rate;
}
// Custom hook for order logic (testable with renderHook)
export function useCheckout() {
const [total, setTotal] = useState(0);
const mutation = useMutation(createOrder);
const checkout = async (items: CartItem[]) => {
const subtotal = calculateOrderTotal(items);
const tax = calculateTax(subtotal);
const finalTotal = subtotal + tax;
await mutation.mutateAsync({ items, total: finalTotal });
setTotal(finalTotal);
};
return { checkout, total, isLoading: mutation.isLoading };
}
// Component (integration testable)
function CheckoutForm() {
const { checkout, total, isLoading } = useCheckout();
return ;
}
```
### Component Design for Testability
| Pattern | Testability | Example |
|---------|-------------|---------|
| Props over context | High | `