Frontend testing is essential for maintaining quality as applications grow. Our development team at Softechinfra shares strategies for comprehensive testing.
Testing Pyramid
💡 The Testing Pyramid
Balance your test suite: Unit tests (70%) for speed, Integration tests (20%) for component interactions, E2E tests (10%) for critical user journeys.
Unit Testing
Testing Library Approach
import { render, screen } from '@testing-library/react';
import { Button } from './Button';describe('Button', () => {
it('renders children correctly', () => {
render();
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const handleClick = jest.fn();
render();
screen.getByRole('button').click();
expect(handleClick).toHaveBeenCalledTimes(1);
});
it('is disabled when disabled prop is true', () => {
render();
expect(screen.getByRole('button')).toBeDisabled();
});
});
Best Practices
What to Unit Test
Integration Testing
Component Integration
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { SearchForm } from './SearchForm';
import { SearchResults } from './SearchResults';
import { SearchPage } from './SearchPage';describe('SearchPage', () => {
it('shows results when search is submitted', async () => {
render( );
await userEvent.type(
screen.getByRole('textbox'),
'test query'
);
await userEvent.click(screen.getByRole('button', { name: /search/i }));
await waitFor(() => {
expect(screen.getByText(/results for/i)).toBeInTheDocument();
});
});
});
API Integration
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { render, screen, waitFor } from '@testing-library/react';
import { UserProfile } from './UserProfile';const server = setupServer(
rest.get('/api/user', (req, res, ctx) => {
return res(ctx.json({ name: 'John Doe', email: 'john@example.com' }));
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe('UserProfile', () => {
it('displays user data from API', async () => {
render( );
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
});
});
E2E Testing
Playwright Example
import { test, expect } from '@playwright/test';test.describe('Checkout Flow', () => {
test('completes purchase successfully', async ({ page }) => {
await page.goto('/products');
// Add to cart
await page.click('[data-testid="product-1"]');
await page.click('button:has-text("Add to Cart")');
// Go to checkout
await page.click('[data-testid="cart-icon"]');
await page.click('button:has-text("Checkout")');
// Fill form
await page.fill('#email', 'test@example.com');
await page.fill('#card-number', '4242424242424242');
await page.fill('#card-expiry', '12/25');
await page.fill('#card-cvc', '123');
// Submit
await page.click('button:has-text("Pay")');
// Verify
await expect(page.locator('h1')).toHaveText('Thank you!');
});
});
Cypress Alternative
describe('Login Flow', () => {
it('logs in successfully', () => {
cy.visit('/login');
cy.get('#email').type('user@example.com');
cy.get('#password').type('password123');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
});
});Testing Hooks
Custom Hook Testing
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';describe('useCounter', () => {
it('increments counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});
Visual Testing
Snapshot Testing
import { render } from '@testing-library/react';
import { Card } from './Card';it('matches snapshot', () => {
const { container } = render(
);
expect(container).toMatchSnapshot();
});
Visual Regression
Tools: Chromatic, Percy, Playwright
Test Organization
File Structure
src/
components/
Button/
Button.tsx
Button.test.tsx
Button.stories.tsx
utils/
format.ts
format.test.ts
e2e/
checkout.spec.ts
login.spec.tsCI/CD Integration
# GitHub Actions
name: Tests
on: [push, pull_request]jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run test:unit
- run: npm run test:integration
- run: npm run test:e2e
Conclusion
"A balanced testing strategy catches bugs early and maintains confidence. Start with unit tests, add integration tests for critical flows, and use E2E for key user journeys."— Rishikesh Baidya, Lead Developer
Comprehensive testing is essential for maintaining quality at scale. We implement testing strategies for projects like TalkDrill and ExamReady. See our TypeScript best practices for more development insights.
Need Testing Implementation Help?
Our team implements comprehensive testing strategies for React and modern web applications with Jest, Testing Library, and Playwright.
Improve Your Testing →