πŸ”¬
Testing

Testing React Native Apps: Complete Guide οΏ½

Comprehensive guide to testing your React Native applications with Jest, React Native Testing Library, and Detox. Bug-free apps ahead!

Alex ThompsonAlex Thompson
January 3, 2024
18 min read
Testing
Jest
React Native Testing Library
Detox
Quality Assurance

Testing React Native Apps: Complete Guide πŸ§ͺ

Testing is crucial for building reliable React Native apps. Let's dive into a comprehensive testing strategy that will make your apps bulletproof!

Unit Testing with Jest

Start with testing individual functions and components:

// utils.test.js
import { formatCurrency, validateEmail } from '../utils';

describe('Utility Functions', () => {
  test('formatCurrency formats numbers correctly', () => {
    expect(formatCurrency(1234.56)).toBe('$1,234.56');
    expect(formatCurrency(0)).toBe('$0.00');
  });

  test('validateEmail validates email addresses', () => {
    expect(validateEmail('test@example.com')).toBe(true);
    expect(validateEmail('invalid-email')).toBe(false);
  });
});

Component Testing

Test your React components with React Native Testing Library:

// LoginForm.test.js
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import LoginForm from '../LoginForm';

describe('LoginForm', () => {
  test('renders login form correctly', () => {
    const { getByPlaceholderText, getByText } = render(<LoginForm />);
    
    expect(getByPlaceholderText('Email')).toBeTruthy();
    expect(getByPlaceholderText('Password')).toBeTruthy();
    expect(getByText('Login')).toBeTruthy();
  });

  test('calls onLogin with correct credentials', async () => {
    const mockOnLogin = jest.fn();
    const { getByPlaceholderText, getByText } = render(
      <LoginForm onLogin={mockOnLogin} />
    );

    fireEvent.changeText(getByPlaceholderText('Email'), 'test@example.com');
    fireEvent.changeText(getByPlaceholderText('Password'), 'password123');
    fireEvent.press(getByText('Login'));

    await waitFor(() => {
      expect(mockOnLogin).toHaveBeenCalledWith({
        email: 'test@example.com',
        password: 'password123',
      });
    });
  });
});

Integration Testing

Test how components work together:

// UserProfile.test.js
import React from 'react';
import { render, waitFor } from '@testing-library/react-native';
import { UserProvider } from '../context/UserContext';
import UserProfile from '../UserProfile';

const mockUser = {
  id: 1,
  name: 'John Doe',
  email: 'john@example.com',
};

const renderWithProvider = (component) => {
  return render(
    <UserProvider value={{ user: mockUser }}>
      {component}
    </UserProvider>
  );
};

describe('UserProfile Integration', () => {
  test('displays user information correctly', async () => {
    const { getByText } = renderWithProvider(<UserProfile />);

    await waitFor(() => {
      expect(getByText('John Doe')).toBeTruthy();
      expect(getByText('john@example.com')).toBeTruthy();
    });
  });
});

E2E Testing with Detox

Test the complete user journey:

// e2e/login.e2e.js
describe('Login Flow', () => {
  beforeAll(async () => {
    await device.launchApp();
  });

  beforeEach(async () => {
    await device.reloadReactNative();
  });

  it('should login successfully with valid credentials', async () => {
    await element(by.id('email-input')).typeText('test@example.com');
    await element(by.id('password-input')).typeText('password123');
    await element(by.id('login-button')).tap();

    await expect(element(by.text('Welcome!'))).toBeVisible();
  });

  it('should show error with invalid credentials', async () => {
    await element(by.id('email-input')).typeText('invalid@example.com');
    await element(by.id('password-input')).typeText('wrongpassword');
    await element(by.id('login-button')).tap();

    await expect(element(by.text('Invalid credentials'))).toBeVisible();
  });
});

Mocking External Dependencies

Mock API calls and external services:

// __mocks__/api.js
export const authAPI = {
  login: jest.fn(() => Promise.resolve({ token: 'mock-token' })),
  logout: jest.fn(() => Promise.resolve()),
  getProfile: jest.fn(() => Promise.resolve(mockUser)),
};

// In your test file
import { authAPI } from '../api';

jest.mock('../api');

describe('Auth Service', () => {
  test('handles login success', async () => {
    authAPI.login.mockResolvedValue({ token: 'test-token' });
    
    const result = await login('test@example.com', 'password');
    expect(result.token).toBe('test-token');
  });
});

Testing Best Practices

  1. Follow the Testing Pyramid - More unit tests, fewer E2E tests
  2. Test behavior, not implementation
  3. Use descriptive test names
  4. Keep tests independent
  5. Mock external dependencies
  6. Test error scenarios

Coverage Reports

Set up coverage reporting:

// package.json
{
  "scripts": {
    "test:coverage": "jest --coverage"
  },
  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,jsx}",
      "!src/**/*.test.{js,jsx}",
      "!src/index.js"
    ],
    "coverageThreshold": {
      "global": {
        "branches": 80,
        "functions": 80,
        "lines": 80,
        "statements": 80
      }
    }
  }
}

Happy testing! Your users will thank you for the bug-free experience! πŸ§ͺ

Was this page helpful?

Help us improve our documentation