puppeteer vs playwright vs selenium-webdriver
モダンなE2Eテストとブラウザ自動化のためのツール選択
puppeteerplaywrightselenium-webdriver類似パッケージ:

モダンなE2Eテストとブラウザ自動化のためのツール選択

playwrightpuppeteerselenium-webdriver はすべてブラウザの自動操作を可能にするnpmパッケージであり、主にエンドツーエンド(E2E)テストやスクレイピング、自動化タスクに使われます。これらはそれぞれ異なるアプローチでブラウザ制御を実現しており、サポートするブラウザ範囲、API設計、安定性、デバッグ機能などに明確な違いがあります。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
puppeteer8,442,06993,73763 kB2863日前Apache-2.0
playwright083,6863.72 MB6111ヶ月前Apache-2.0
selenium-webdriver034,08917.9 MB20416日前Apache-2.0

Playwright vs Puppeteer vs Selenium WebDriver:E2Eテストとブラウザ自動化の実践的比較

フロントエンド開発者がE2Eテストやブラウザ自動化を実装する際、playwrightpuppeteerselenium-webdriver の3つが代表的な選択肢になります。これらは目的こそ似ていますが、設計思想や実装方法、サポート範囲に大きな違いがあります。現場で実際に使う観点から、深く掘り下げて比較します。

🌐 サポートブラウザ:1つだけ vs 複数 vs 全般

puppeteerChromium系のみ をサポートします。つまり、ChromeやMicrosoft Edge(Chromium版)は動作しますが、FirefoxやSafariは対象外です。

// puppeteer: Chromium系限定
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');

playwrightChromium、Firefox、WebKit(Safariのエンジン)の3つを公式サポート しています。1つのテストコードで複数ブラウザでの動作確認が可能です。

// playwright: ブラウザごとに起動可能
const { chromium, firefox, webkit } = require('playwright');

const browser = await chromium.launch(); // または firefox.launch(), webkit.launch()
const page = await browser.newPage();
await page.goto('https://example.com');

selenium-webdriver は理論上 すべてのWebDriver準拠ブラウザ をサポートしますが、各ブラウザ用のドライバー(chromedriver、geckodriverなど)を別途インストール・管理する必要があります。

// selenium-webdriver: ドライバー依存
const { Builder, By } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');

let driver = await new Builder()
  .forBrowser('chrome')
  .setChromeOptions(new chrome.Options())
  .build();

await driver.get('https://example.com');

💡 実務では、FirefoxやSafariでの動作検証が必要なら playwright 一択。Chromium系だけで十分なら puppeteer の軽さが魅力。レガシー環境やマルチ言語対応が必須なら selenium-webdriver を検討。

⚙️ API設計:直感的 vs 冗長

puppeteerplaywright はどちらもモダンで一貫性のあるPromiseベースのAPIを持ち、page.click()page.fill() のように直感的に操作できます。

// puppeteer
await page.goto('https://example.com');
await page.type('#username', 'testuser');
await page.click('#submit');

// playwright
await page.goto('https://example.com');
await page.fill('#username', 'testuser');
await page.click('#submit');

一方、selenium-webdriver はWebDriverプロトコルに忠実なため、要素の取得と操作が分かれており、コードが冗長になりがちです。

// selenium-webdriver
await driver.get('https://example.com');
let username = await driver.findElement(By.id('username'));
await username.sendKeys('testuser');
let submit = await driver.findElement(By.id('submit'));
await submit.click();

また、selenium-webdriver は非同期処理を async/await で書くこともできますが、内部でコマンドキューを管理するため、エラーハンドリングやデバッグがやや複雑です。

🧪 テスト安定性:待機戦略の違い

不安定なE2Eテストの最大の原因は「タイミング」です。各ツールはこの問題に対し、異なるアプローチを取っています。

puppeteerpage.waitForSelector()page.waitForFunction() を使って明示的に待機する必要があります。

// puppeteer: 手動待機
await page.click('#load-data');
await page.waitForSelector('#result'); // 要素が表示されるまで待つ

playwright は多くのアクション(clickfill など)に 自動待機機能 が組み込まれており、要素が操作可能になるまで内部で待機します。

// playwright: 自動待機
await page.click('#load-data');
await page.click('#result'); // 要素がクリック可能になるまで自動で待つ

selenium-webdriverWebDriverWait のような明示的待機(Explicit Wait)を推奨しており、Implicit Wait(暗黙的待機)は非推奨です。

// selenium-webdriver: 明示的待機
const until = require('selenium-webdriver/until');

await driver.findElement(By.id('load-data')).click();
await driver.wait(until.elementLocated(By.id('result')), 5000);

💡 実務では、playwright の自動待機がテストの安定性を大幅に向上させ、メンテナンスコストを削減します。puppeteerselenium-webdriver では待機ロジックの記述漏れがバグの温床になりやすいです。

📱 モバイル・ネットワークエミュレーション

モバイル端末や低速ネットワーク下での動作確認が必要な場合、ツール間でサポート状況が大きく異なります。

playwright はビルドインで デバイスエミュレーションネットワークスロットリング をサポートしています。

// playwright: デバイスエミュレーション
const { devices } = require('playwright');
const iPhone = devices['iPhone 13'];

const context = await browser.newContext({ ...iPhone });
const page = await context.newPage();
// playwright: ネットワーク制限
await context.setOffline(true);
await context.setGeolocation({ latitude: 35.6895, longitude: 139.6917 });

puppeteer もChromiumの機能を活用して同様のことが可能ですが、設定がやや低レベルです。

// puppeteer: ネットワーク制限
const client = await page.target().createCDPSession();
await client.send('Network.emulateNetworkConditions', {
  offline: false,
  latency: 100,
  downloadThroughput: 1.5 * 1024 * 1024 / 8,
  uploadThroughput: 1.5 * 1024 * 1024 / 8
});

selenium-webdriver は標準ではこれらの機能を提供せず、ブラウザ固有の拡張(例:Chrome DevTools Protocol経由)を使う必要があります。そのため、設定が複雑でポータビリティが低いです。

🛠️ デバッグと開発体験

playwrightPWDEBUG=1 環境変数でGUI付きデバッガーを起動でき、タイムトラベルデバッグやスクリーンショット比較も可能です。

PWDEBUG=1 npm test

puppeteerheadless: false オプションでブラウザを可視化できますが、高度なデバッグ機能は備えていません。

// puppeteer: 可視化
const browser = await puppeteer.launch({ headless: false });

selenium-webdriver も同様にヘッドフルモードで実行可能ですが、デバッグ支援機能は最小限です。

📦 インストールとセットアップの手間

puppeteerplaywrightnpm install 時に必要なブラウザバイナリを自動でダウンロードします。追加設定不要で即座に使い始められます。

npm install playwright
# → Chromium, Firefox, WebKit が自動インストール

selenium-webdriver はブラウザドライバーを別途管理する必要があります。たとえばChromeを使うには chromedriver をインストールし、PATHを通すか、コード内でパスを指定する必要があります。

// selenium-webdriver: ドライバーの明示的指定
const service = new chrome.ServiceBuilder('/path/to/chromedriver').build();
chrome.setDefaultService(service);

これはCI環境でのセットアップを煩雑にし、チーム内での環境差異を生む原因になります。

🔄 非推奨状況と将来性

2024年現在、いずれのパッケージも 非推奨ではありませんpuppeteer はGoogleが、playwright はMicrosoftが、selenium-webdriver はSeleniumプロジェクトがそれぞれ積極的にメンテナンスしています。

ただし、puppeteer は2023年にコミュニティ主導のプロジェクトとなり、Googleによる直接的な開発は終了しました。一方、playwright は活発な開発が続いており、新機能(例:Component Testing、Trace Viewer)が継続的に追加されています。

📊 まとめ:選択の指針

観点playwrightpuppeteerselenium-webdriver
サポートブラウザChromium, Firefox, WebKitChromium系のみ全WebDriver準拠ブラウザ(ドライバー要)
APIの使いやすさ非常に直感的(自動待機付き)直感的だが手動待機必要冗長(要素取得と操作が分離)
モバイル/ネットワークエミュレートビルドインで簡単可能だが低レベル標準では不可、ブラウザ依存
デバッグ支援GUIデバッガー、トレース機能ヘッドフルモードのみ最小限
セットアップの手間自動(npm installで完了)自動ドライバー管理が必要
最適なユースケース本格的なE2Eテスト、クロスブラウザ検証スクレイピング、Chromium限定の自動化マルチ言語環境、既存Seleniumインフラとの統合

💡 最終的なアドバイス

  • 新規プロジェクトでE2Eテストを始めるなら、まず playwright を試すべきです。安定性、機能、開発体験のすべてで現代的なニーズに応えています。
  • Chromium系だけで十分で、軽量さを重視するなら puppeteer も有効。特にスクレイピングやPDF生成など、テスト以外の自動化タスクに向いています。
  • selenium-webdriver は、既にSeleniumエコシステムに投資している組織や、複数言語でテストを統一したい場合に限って検討してください。純粋なNode.jsプロジェクトではオーバーヘッドが大きすぎます。

これらのツールは「どれが絶対に優れている」というより、「どんな課題を解決したいか」によって最適解が変わります。あなたのチームの技術スタック、テスト要件、保守コストを見極めて、賢く選んでください。

選び方: puppeteer vs playwright vs selenium-webdriver

  • puppeteer:

    puppeteer を選ぶべきなのは、Chromium系ブラウザ(ChromeやEdge)のみを対象とし、シンプルで軽量な自動化スクリプトやスクレイピングを実装したい場合です。Playwrightほどの多機能さは不要で、既存のNode.js環境に手軽に組み込みたいケースに向いています。

  • playwright:

    playwright を選ぶべきなのは、複数のブラウザ(Chromium、Firefox、WebKit)でのテストが必要な場合、または最新のWeb標準やモバイルエミュレーション、ネットワーク条件のシミュレーションといった高度な機能を活用したいときです。特に大規模なE2EテストスイートやCI/CD環境での安定した実行を求めるプロジェクトに適しています。

  • selenium-webdriver:

    selenium-webdriver を選ぶべきなのは、JavaやPythonなど複数の言語環境で同じテストコードを共有したい場合、あるいは既にSelenium Gridなどのインフラを運用している組織において、JavaScriptベースのテストも統合したいときです。ただし、Node.js専用のユースケースでは他の2つより冗長になることが多いです。

puppeteer のREADME

Puppeteer

build npm puppeteer package

Puppeteer is a JavaScript library which provides a high-level API to control Chrome or Firefox over the DevTools Protocol or WebDriver BiDi. Puppeteer runs in the headless (no visible UI) by default

Get started | API | FAQ | Contributing | Troubleshooting

Installation

npm i puppeteer # Downloads compatible Chrome during installation.
npm i puppeteer-core # Alternatively, install as a library, without downloading Chrome.

MCP

Install chrome-devtools-mcp, a Puppeteer-based MCP server for browser automation and debugging.

Example

import puppeteer from 'puppeteer';
// Or import puppeteer from 'puppeteer-core';

// Launch the browser and open a new blank page.
const browser = await puppeteer.launch();
const page = await browser.newPage();

// Navigate the page to a URL.
await page.goto('https://developer.chrome.com/');

// Set screen size.
await page.setViewport({width: 1080, height: 1024});

// Open the search menu using the keyboard.
await page.keyboard.press('/');

// Type into search box using accessible input name.
await page.locator('::-p-aria(Search)').fill('automate beyond recorder');

// Wait and click on first result.
await page.locator('.devsite-result-item-link').click();

// Locate the full title with a unique string.
const textSelector = await page
  .locator('::-p-text(Customize and automate)')
  .waitHandle();
const fullTitle = await textSelector?.evaluate(el => el.textContent);

// Print the full title.
console.log('The title of this blog post is "%s".', fullTitle);

await browser.close();