0%
Les Tests dans React

Tests

Qualité et maintenance

10-15 min

Les Tests dans React

Les tests sont essentiels pour garantir la qualité et la maintenabilité de vos applications React. Nous allons explorer les différents types de tests et les outils disponibles.

Configuration de base

Create React App inclut déjà Jest et React Testing Library. Pour les projets existants :

npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event

Tests unitaires

Test d’un composant simple

// Button.js
function Button({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}

// Button.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';

test('rendre le bouton avec le bon texte', () => {
  render(<Button>Cliquez-moi</Button>);
  expect(screen.getByText('Cliquez-moi')).toBeInTheDocument();
});

test('appeler onClick quand le bouton est cliqué', () => {
  const handleClick = jest.fn();
  render(<Button onClick={handleClick}>Cliquez-moi</Button>);
  
  fireEvent.click(screen.getByText('Cliquez-moi'));
  expect(handleClick).toHaveBeenCalledTimes(1);
});

Test avec des hooks

// useCounter.js
function useCounter(initialValue = 0) {
  const [count, setCount] = useState(initialValue);
  const increment = () => setCount(count + 1);
  const decrement = () => setCount(count - 1);
  return { count, increment, decrement };
}

// useCounter.test.js
import { renderHook, act } from '@testing-library/react-hooks';
import { useCounter } from './useCounter';

test('incrémenter le compteur', () => {
  const { result } = renderHook(() => useCounter(0));

  act(() => {
    result.current.increment();
  });

  expect(result.current.count).toBe(1);
});

Tests d’intégration

Test d’un formulaire

// LoginForm.js
function LoginForm({ onSubmit }) {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    onSubmit({ email, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <button type="submit">Se connecter</button>
    </form>
  );
}

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

test('soumission du formulaire avec les bonnes données', async () => {
  const handleSubmit = jest.fn();
  render(<LoginForm onSubmit={handleSubmit} />);

  fireEvent.change(screen.getByLabelText(/email/i), {
    target: { value: 'test@example.com' },
  });
  fireEvent.change(screen.getByLabelText(/password/i), {
    target: { value: 'password123' },
  });
  fireEvent.click(screen.getByText(/se connecter/i));

  expect(handleSubmit).toHaveBeenCalledWith({
    email: 'test@example.com',
    password: 'password123',
  });
});

Tests de composants avec Redux

// Counter.js
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './counterSlice';

function Counter() {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <p>Compteur : {count}</p>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  );
}

// Counter.test.js
import { render, screen, fireEvent } from '@testing-library/react';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
import Counter from './Counter';

const createTestStore = (initialState) => {
  return configureStore({
    reducer: {
      counter: counterReducer,
    },
    preloadedState: initialState,
  });
};

test('incrémenter le compteur avec Redux', () => {
  const store = createTestStore({ counter: { value: 0 } });
  
  render(
    <Provider store={store}>
      <Counter />
    </Provider>
  );

  fireEvent.click(screen.getByText('+'));
  expect(screen.getByText('Compteur : 1')).toBeInTheDocument();
});

Tests d’accessibilité

// AccessibleButton.js
function AccessibleButton({ onClick, children }) {
  return (
    <button
      onClick={onClick}
      aria-label="Action importante"
      role="button"
      tabIndex={0}
    >
      {children}
    </button>
  );
}

// AccessibleButton.test.js
import { render, screen } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import AccessibleButton from './AccessibleButton';

expect.extend(toHaveNoViolations);

test('le bouton est accessible', async () => {
  const { container } = render(
    <AccessibleButton onClick={() => {}}>Cliquez-moi</AccessibleButton>
  );

  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

Bonnes pratiques

  1. Organisation des tests :

    • Un fichier de test par composant
    • Regroupez les tests par fonctionnalité
    • Utilisez des descriptions claires
  2. Sélection des éléments :

    • Préférez les sélecteurs accessibles
    • Évitez les sélecteurs basés sur la structure
    • Utilisez des attributs data-testid avec modération
  3. Couverture des tests :

    • Testez les cas d’utilisation principaux
    • Incluez les cas d’erreur
    • Testez les interactions utilisateur

Exercices pratiques

  1. Créez des tests pour un composant de formulaire qui :

    • Valide les entrées
    • Gère les erreurs
    • Soumet les données
  2. Créez des tests pour un composant de liste qui :

    • Affiche les éléments
    • Permet la sélection
    • Gère le tri
  3. Créez des tests pour un composant de navigation qui :

    • Affiche les liens
    • Gère la navigation active
    • Est accessible

Conclusion

Les tests sont essentiels pour maintenir la qualité de votre code. Dans le prochain chapitre, nous allons explorer le déploiement de vos applications React.

Commentaires

Les commentaires sont alimentés par GitHub Discussions

Connectez-vous avec GitHub pour participer à la discussion

Lien copié !