A robust testing strategy is essential for maintaining code quality and shipping with confidence. This guide covers the testing pyramid for frontend applications, from unit tests to end-to-end testing.
Unit Testing with Vitest
// Vitest unit test example
import { describe, it, expect, vi } from 'vitest';
import { calculateTotal, formatCurrency } from './utils';
describe('calculateTotal', () => {
it('calculates sum of item prices', () => {
const items = [
{ price: 10, quantity: 2 },
{ price: 5, quantity: 1 },
];
expect(calculateTotal(items)).toBe(25);
});
it('applies discount correctly', () => {
const items = [{ price: 100, quantity: 1 }];
expect(calculateTotal(items, { discount: 0.1 })).toBe(90);
});
it('returns 0 for empty array', () => {
expect(calculateTotal([])).toBe(0);
});
});Component Testing
// React Testing Library example
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ProductCard } from './ProductCard';
describe('ProductCard', () => {
const product = {
id: '1',
name: 'Test Product',
price: 99.99,
image: '/test.jpg',
};
it('renders product information', () => {
render(<ProductCard product={product} />);
expect(screen.getByText('Test Product')).toBeInTheDocument();
expect(screen.getByText('$99.99')).toBeInTheDocument();
});
it('calls onAddToCart when button clicked', async () => {
const onAddToCart = vi.fn();
const user = userEvent.setup();
render(<ProductCard product={product} onAddToCart={onAddToCart} />);
await user.click(screen.getByRole('button', { name: /add to cart/i }));
expect(onAddToCart).toHaveBeenCalledWith(product.id);
});
});E2E Testing with Playwright
// Playwright E2E test
import { test, expect } from '@playwright/test';
test.describe('Checkout Flow', () => {
test('completes purchase successfully', async ({ page }) => {
await page.goto('/products');
// Add item to cart
await page.click('[data-testid="product-1"] button');
// Go to cart
await page.click('[data-testid="cart-icon"]');
await expect(page.locator('[data-testid="cart-count"]')).toHaveText('1');
// Checkout
await page.click('text=Checkout');
await page.fill('[name="email"]', 'test@example.com');
await page.fill('[name="card"]', '4242424242424242');
await page.click('text=Pay Now');
// Verify success
await expect(page).toHaveURL(/\/order-confirmation/);
await expect(page.locator('h1')).toContainText('Order Confirmed');
});
});Testing Strategy
Testing Pyramid
Unit Tests (70%)
- Pure functions and utilities
- Fast execution, high coverage
Integration Tests (20%)
- Component interactions
- API integrations
E2E Tests (10%)
- Critical user flows
- Cross-browser testing
Conclusion
A balanced testing strategy combines fast unit tests, thorough integration tests, and targeted E2E tests. Focus on testing behavior, not implementation, and maintain tests as features evolve.
Need help improving your testing strategy? Contact Jishu Labs for expert frontend development consulting.
About Emily Zhang
Emily Zhang is the Frontend Lead at Jishu Labs with deep expertise in frontend testing and quality assurance.