Les tests mobiles couvrent les interactions tactiles, la fragmentation des appareils, la simulation des conditions réseau, les contraintes WebKit sur iOS et les compromis entre appareils réels et émulateurs. Chacun de ces aspects demande des techniques différentes du test web desktop.

Défis spécifiques au mobile

Fragmentation des appareils : Android tourne sur des milliers d'appareils différents, avec des tailles d'écran, versions d'OS et configurations matérielles variées. iOS est plus homogène, mais reste fragmenté sur les tailles et versions. Interactions tactiles : Sur mobile, on tape, glisse, pince et pivote. Les tests basés sur le clic souris ne couvrent pas ces interactions. Conditions réseau : Les utilisateurs mobiles fonctionnent souvent avec des connexions lentes ou instables (3G, 4G, Wi-Fi capricieux). L'application doit gérer ces conditions sans planter. Contraintes de performance : Moins de RAM, processeurs plus lents, batteries limitées. Ce qui est rapide sur desktop peut être lent sur mobile. Différences de plateforme : iOS et Android gèrent différemment les notifications, les deep links, le partage de fichiers, l'accès caméra et les notifications push. Le problème WebKit sur iOS : Tous les navigateurs iOS utilisent WebKit en interne. Chrome sur iPhone n'est pas Chrome. C'est le moteur de Safari enveloppé dans une interface Chrome. Un bug WebKit affecte tous les navigateurs iOS sans exception.

Types de tests mobiles

Tests web responsive

Tester l'application web dans des viewports mobiles, sans installation d'application.

// Playwright — test sur viewport mobile
test('la page de connexion fonctionne sur mobile', async ({ page }) => {
  await page.setViewportSize({ width: 375, height: 667 });  // iPhone SE
  await page.goto('/login');
  
  // L'interface mobile doit apparaître
  await expect(page.getByTestId('mobile-header')).toBeVisible();
  await expect(page.getByTestId('desktop-nav')).not.toBeVisible();
  
  // Les fonctionnalités de base fonctionnent
  await page.fill('[data-testid="email"]', 'user@test.com');
  await page.fill('[data-testid="password"]', 'ValidPass1');
  await page.tap('[data-testid="submit"]');  // tap plutôt que click
  
  await expect(page).toHaveURL('/dashboard');
});

Tests d'applications natives

Applications installées depuis l'App Store ou Google Play. Nécessite Appium, Detox (React Native) ou XCUITest/Espresso directement.

Tests Progressive Web App (PWA)

Applications web qui se comportent comme des applications natives : installables, fonctionnelles hors ligne, capables d'envoyer des notifications push.

Playwright pour les tests web mobiles

L'émulation de périphériques de Playwright couvre les scénarios de test mobile les plus courants pour les applications web :

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  projects: [
    // Desktop
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    
    // Mobile
    { name: 'mobile-chrome', use: { ...devices['Pixel 7'] } },
    { name: 'mobile-safari', use: { ...devices['iPhone 14'] } },
    { name: 'tablet', use: { ...devices['iPad Pro'] } },
  ],
});

Appareils disponibles (voir npx playwright show-devices) : iPhone 14, iPhone 14 Pro, iPhone SE, Pixel 7, Galaxy S21, iPad Pro et iPad Mini.

L'émulation configure automatiquement la taille du viewport, le user agent, le ratio pixel de l'appareil et la prise en charge des événements tactiles.

Événements tactiles vs événements souris

Sur mobile, on tape. Playwright's click() fonctionne dans les deux cas, mais certains scénarios demandent un toucher explicite :

test('le carousel swipe fonctionne', async ({ page }) => {
  await page.setViewportSize({ width: 375, height: 812 });
  await page.goto('/products');
  
  const carousel = page.getByTestId('product-carousel');
  const box = await carousel.boundingBox();
  
  if (box) {
    // Simuler un swipe vers la gauche (le doigt se déplace de droite à gauche)
    await page.touchscreen.tap(box.x + box.width - 50, box.y + box.height / 2);
    await page.mouse.move(box.x + box.width - 50, box.y + box.height / 2);
    await page.mouse.down();
    await page.mouse.move(box.x + 50, box.y + box.height / 2, { steps: 20 });
    await page.mouse.up();
  }
  
  // Vérifier que la diapositive suivante est visible
  await expect(page.getByTestId('slide-2')).toBeVisible();
});

Tester les mises en page responsive

Vérifier que l'interface s'adapte correctement aux différents points de rupture :

const viewports = [
  { name: 'mobile', width: 375, height: 667 },
  { name: 'mobile-large', width: 414, height: 896 },
  { name: 'tablet', width: 768, height: 1024 },
  { name: 'desktop', width: 1280, height: 720 },
];

for (const viewport of viewports) {
  test(`la navigation s'affiche correctement sur ${viewport.name}`, async ({ page }) => {
    await page.setViewportSize({ width: viewport.width, height: viewport.height });
    await page.goto('/');
    
    if (viewport.width < 768) {
      // Mobile : menu hamburger
      await expect(page.getByTestId('hamburger')).toBeVisible();
      await expect(page.getByTestId('desktop-nav')).not.toBeVisible();
    } else {
      // Desktop/tablette : navigation complète
      await expect(page.getByTestId('hamburger')).not.toBeVisible();
      await expect(page.getByTestId('desktop-nav')).toBeVisible();
    }
  });
}

Limitation de la bande passante

Tester le comportement de l'application sur des réseaux mobiles lents :

test('l\'application charge sur une 3G lente', async ({ page, context }) => {
  // Limiter à la vitesse 3G
  const cdpSession = await context.newCDPSession(page);
  await cdpSession.send('Network.emulateNetworkConditions', {
    offline: false,
    downloadThroughput: 780 * 1024 / 8,  // 780 kbps 3G download
    uploadThroughput: 330 * 1024 / 8,    // 330 kbps 3G upload
    latency: 100,                          // 100ms de latence
  });
  
  const startTime = Date.now();
  await page.goto('/');
  const loadTime = Date.now() - startTime;
  
  // La page doit charger en moins de 10 secondes sur 3G
  expect(loadTime).toBeLessThan(10000);
  
  await expect(page.getByTestId('main-content')).toBeVisible();
});

test('l\'application fonctionne hors ligne (PWA)', async ({ page, context }) => {
  await page.goto('/');
  
  // Passer hors ligne
  await context.setOffline(true);
  
  await page.reload();
  
  // La PWA doit afficher du contenu en cache ou une page hors ligne
  await expect(page.getByTestId('offline-message')).toBeVisible();
  // OU
  await expect(page.getByTestId('main-content')).toBeVisible();  // Contenu en cache
  
  // Repasser en ligne
  await context.setOffline(false);
});

Tests sur appareils réels avec BrowserStack

L'émulation couvre la plupart des scénarios. Pour les appareils réels, les services cloud principaux sont BrowserStack (iOS et Android dans le cloud), Sauce Labs (offre similaire) et Firebase Test Lab (parc de Google, orienté Android).

// Configuration Playwright pour les appareils réels BrowserStack
export default defineConfig({
  projects: [
    {
      name: 'real-iphone-14',
      use: {
        connectOptions: {
          wsEndpoint: `wss://cdp.browserstack.com/playwright?caps=${encodeURIComponent(JSON.stringify({
            'browserstack.user': process.env.BS_USER,
            'browserstack.key': process.env.BS_KEY,
            'browserName': 'safari',
            'bstack:options': {
              deviceName: 'iPhone 14',
              osVersion: '16',
            }
          }))}`,
        },
      },
    },
  ],
});

Bugs mobiles courants à tester

Taille des zones de tap : Des boutons trop petits pour être touchés avec fiabilité (moins de 44x44px sur iOS, 48x48dp sur Android).

test('tous les éléments interactifs sont assez grands pour être touchés', async ({ page }) => {
  await page.setViewportSize({ width: 375, height: 667 });
  await page.goto('/');
  
  const buttons = await page.locator('button, a, [role="button"]').all();
  
  for (const button of buttons) {
    const box = await button.boundingBox();
    if (box) {
      expect(box.width, `Bouton trop étroit : ${await button.textContent()}`).toBeGreaterThanOrEqual(44);
      expect(box.height, `Bouton trop court : ${await button.textContent()}`).toBeGreaterThanOrEqual(44);
    }
  }
});

Défilement horizontal : Le contenu ne doit pas nécessiter de défilement horizontal sur mobile.

test('pas de défilement horizontal sur mobile', async ({ page }) => {
  await page.setViewportSize({ width: 375, height: 667 });
  await page.goto('/');
  
  const scrollWidth = await page.evaluate(() => document.documentElement.scrollWidth);
  const clientWidth = await page.evaluate(() => document.documentElement.clientWidth);
  
  expect(scrollWidth).toBe(clientWidth);  // Pas de dépassement horizontal
});

Zoom automatique des inputs : Sur iOS, un input avec une taille de police inférieure à 16px déclenche un zoom automatique. Minimum 16px pour éviter ce comportement.

Checklist de tests mobiles

Avant chaque release :

  • [ ] Parcours principaux testés sur iPhone (Safari) et Android (Chrome)
  • [ ] Mise en page responsive vérifiée à 375px, 768px, 1280px
  • [ ] Zones de tap d'au moins 44x44px
  • [ ] Pas de défilement horizontal sur les viewports mobiles
  • [ ] Formulaires utilisables avec les claviers mobiles
  • [ ] États de chargement visibles sur les connexions lentes
  • [ ] Images correctement dimensionnées pour le mobile
  • [ ] Texte lisible sans zoom (minimum 16px)

Récapitulatif

| Type de test | Outil | Quand l'utiliser |

|---|---|---|

| Web responsive | Viewport Playwright | Chaque sprint |

| Cross-browser mobile | Presets devices Playwright | Avant chaque release |

| Limitation réseau | Chrome DevTools Protocol via Playwright | Fonctionnalités à usage mobile intensif |

| Appareils réels | BrowserStack, Sauce Labs | Avant les releases majeures |

| Applications natives | Appium, Detox | Projet natif séparé |

Pour la plupart des applications web, les tests responsive couvrent 80 % de la valeur des tests mobiles. Les vérifications sur appareils réels sont à réserver à la validation pré-release et aux bugs connus liés à des plateformes spécifiques, notamment le comportement de Safari iOS.

→ See also: Émulation Mobile dans Playwright: Tests Responsifs et Tactiles | Stratégies de Tests Cross-Browser: Quand et Comment Tester sur Plusieurs Navigateurs | Tests d'Accessibilité avec Playwright: Vérifications a11y Automatisées