PHPおよびSymfonyのためのブラウザテストとWebスクレイピングライブラリ
Pantherは、Webサイトをこすり、実際のブラウザーを使用してエンドツーエンドのテストを実行するための便利なスタンドアロンライブラリです。
パンサーは非常に強力です。 W3CのWebDriverプロトコルを活用して、Google ChromeやFirefoxなどのネイティブWebブラウザーを駆動します。
Pantherは、Symfonyの人気のあるBrowserkitとDomcrawler APIを実装しており、アプリをテストするために必要なすべての機能が含まれているため、非常に使いやすいです。 Symfonyアプリの機能テストを作成したことがある場合は、馴染みがあります。APIはまったく同じです。スタンドアロンライブラリであるため、PantherはすべてのPHPプロジェクトで使用できることに注意してください。
PantherはChromeまたはFirefoxのローカルインストールを自動的に見つけて起動するため、コンピューターに他のものをインストールする必要はありません。Seleniumサーバーは必要ありません。
テストモードでは、PantherはPHPビルトインWebサーバーを使用してアプリケーションを自動的に起動します。テストやWebスクレイピングシナリオの作成に焦点を当てることができ、Pantherは他のすべての世話をします。
あなたが慣れているテストやWebスクレイピングライブラリとは異なり、Panther:
作曲家を使用して、プロジェクトにPantherをインストールします。 Pantherを使用してテストのみを使用し、生産環境でのWebスクレイピングには使用したい場合は、 --dev
フラグを使用することができます。
composer req symfony/panther
composer req --dev symfony/panther
Pantherは、WebDriverプロトコルを使用して、Webサイトをクロールするために使用されるブラウザを制御します。
すべてのシステムでは、 dbrekelmans/browser-driver-installer
使用して、ChromedriverとGeckodriverをローカルにインストールできます。
composer require --dev dbrekelmans/bdi
vendor/bin/bdi detect drivers
Pantherは、 drivers/
ディレクトリに保存されている自動的にドライバーを検出および使用します。
または、オペレーティングシステムのパッケージマネージャーを使用してインストールすることもできます。
ubuntuで、実行:
apt-get install chromium-chromedriver firefox-geckodriver
Macで、Homebrewを使用してください:
brew install chromedriver geckodriver
Windowsで、チョコレートの使用:
choco install chromedriver selenium-gecko-driver
最後に、手動でChromedriver(ChromiumまたはChrome用)とGeckodriver(Firefox用)をダウンロードして、 PATH
またはプロジェクトのdrivers/
ディレクトリに配置できます。
Pantherを使用してアプリケーションをテストする場合は、Panther Phpunit拡張機能を強く登録することをお勧めします。厳密に必須ではありませんが、この拡張機能は、パフォーマンスを向上させ、インタラクティブなデバッグモードを使用できるようにすることで、テストエクスペリエンスを劇的に改善します。
PANTHER_ERROR_SCREENSHOT_DIR
環境変数と併用して拡張機能を使用する場合、故障またはエラー(クライアントが作成された後)のPantherクライアントを使用したテストは、デバッグを支援するためにスクリーンショットを自動的に取得します。
Panther拡張機能を登録するには、次の行をphpunit.xml.dist
に追加します。
<!-- phpunit.xml.dist -->
< extensions >
< extension class = " SymfonyComponentPantherServerExtension " />
</ extensions >
拡張機能なしでは、Pantherがテスト中のアプリケーションを提供するために使用するWebサーバーはオンデマンドで開始され、 tearDownAfterClass()
が呼び出されたときに停止します。一方、拡張機能が登録されると、Webサーバーは最後のテストの後にのみ停止されます。
<?php
use Symfony Component Panther Client ;
require __DIR__ . ' /vendor/autoload.php ' ; // Composer's autoloader
$ client = Client :: createChromeClient ();
// Or, if you care about the open web and prefer to use Firefox
$ client = Client :: createFirefoxClient ();
$ client -> request ( ' GET ' , ' https://api-platform.com ' ); // Yes, this website is 100% written in JavaScript
$ client -> clickLink ( ' Getting started ' );
// Wait for an element to be present in the DOM (even if hidden)
$ crawler = $ client -> waitFor ( ' #installing-the-framework ' );
// Alternatively, wait for an element to be visible
$ crawler = $ client -> waitForVisibility ( ' #installing-the-framework ' );
echo $ crawler -> filter ( ' #installing-the-framework ' )-> text ();
$ client -> takeScreenshot ( ' screen.png ' ); // Yeah, screenshot!
PantherTestCase
クラスを使用すると、E2Eテストを簡単に記述できます。組み込みのPHP Webサーバーを使用してアプリを自動的に起動し、Pantherを使用してクロールすることができます。慣れているすべてのテストツールを提供するために、PhpunitのTestCase
を拡張します。
Symfonyアプリケーションをテストしている場合、 PantherTestCase
WebTestCase
クラスを自動的に拡張します。これは、機能テストを簡単に作成できることを意味します。これにより、アプリケーションのカーネルを直接実行して、既存のすべてのサービスにアクセスできます。この場合、SymfonyがPantherとともに提供したすべてのCrawlerテストアサーションを使用できます。
<?php
namespace App Tests ;
use Symfony Component Panther PantherTestCase ;
class E2eTest extends PantherTestCase
{
public function testMyApp (): void
{
$ client = static :: createPantherClient (); // Your app is automatically started using the built-in web server
$ client -> request ( ' GET ' , ' /mypage ' );
// Use any PHPUnit assertion, including the ones provided by Symfony
$ this -> assertPageTitleContains ( ' My Title ' );
$ this -> assertSelectorTextContains ( ' #main ' , ' My body ' );
// Or the one provided by Panther
$ this -> assertSelectorIsEnabled ( ' .search ' );
$ this -> assertSelectorIsDisabled ( ' [type="submit"] ' );
$ this -> assertSelectorIsVisible ( ' .errors ' );
$ this -> assertSelectorIsNotVisible ( ' .loading ' );
$ this -> assertSelectorAttributeContains ( ' .price ' , ' data-old-price ' , ' 42 ' );
$ this -> assertSelectorAttributeNotContains ( ' .price ' , ' data-old-price ' , ' 36 ' );
// Use waitForX methods to wait until some asynchronous process finish
$ client -> waitFor ( ' .popin ' ); // wait for element to be attached to the DOM
$ client -> waitForStaleness ( ' .popin ' ); // wait for element to be removed from the DOM
$ client -> waitForVisibility ( ' .loader ' ); // wait for element of the DOM to become visible
$ client -> waitForInvisibility ( ' .loader ' ); // wait for element of the DOM to become hidden
$ client -> waitForElementToContain ( ' .total ' , ' 25 € ' ); // wait for text to be inserted in the element content
$ client -> waitForElementToNotContain ( ' .promotion ' , ' 5% ' ); // wait for text to be removed from the element content
$ client -> waitForEnabled ( ' [type="submit"] ' ); // wait for the button to become enabled
$ client -> waitForDisabled ( ' [type="submit"] ' ); // wait for the button to become disabled
$ client -> waitForAttributeToContain ( ' .price ' , ' data-old-price ' , ' 25 € ' ); // wait for the attribute to contain content
$ client -> waitForAttributeToNotContain ( ' .price ' , ' data-old-price ' , ' 25 € ' ); // wait for the attribute to not contain content
// Let's predict the future
$ this -> assertSelectorWillExist ( ' .popin ' ); // element will be attached to the DOM
$ this -> assertSelectorWillNotExist ( ' .popin ' ); // element will be removed from the DOM
$ this -> assertSelectorWillBeVisible ( ' .loader ' ); // element will be visible
$ this -> assertSelectorWillNotBeVisible ( ' .loader ' ); // element will not be visible
$ this -> assertSelectorWillContain ( ' .total ' , ' €25 ' ); // text will be inserted in the element content
$ this -> assertSelectorWillNotContain ( ' .promotion ' , ' 5% ' ); // text will be removed from the element content
$ this -> assertSelectorWillBeEnabled ( ' [type="submit"] ' ); // button will be enabled
$ this -> assertSelectorWillBeDisabled ( ' [type="submit"] ' ); // button will be disabled
$ this -> assertSelectorAttributeWillContain ( ' .price ' , ' data-old-price ' , ' €25 ' ); // attribute will contain content
$ this -> assertSelectorAttributeWillNotContain ( ' .price ' , ' data-old-price ' , ' €25 ' ); // attribute will not contain content
}
}
このテストを実行するには:
bin/phpunit tests/E2eTest.php
また、Pantherは、 Client
とCrawler
の他のブラウザーキットベースの実装に即座にアクセスできます。 Pantherのネイティブクライアントとは異なり、これらの代替クライアントはJavaScript、CSS、Screenshot Capturingをサポートしていませんが、超高速です!
2つの代替クライアントが利用可能です。
WebTestCase
が提供するSymfonyカーネルを直接操作します。利用可能な最速のクライアントですが、Symfonyアプリでのみ利用できます。楽しい部分は、3人のクライアントがまったく同じAPIを実装するため、適切なファクトリーメソッドを呼び出すだけで、1つのテストケースに適したトレードオフをもたらすだけで、1つから切り替えることができることです(JavaScriptが必要ですか?外部SSOサーバーで認証する必要がありますか?
これらのクライアントのインスタンスを取得する方法は次のとおりです。
<?php
namespace App Tests ;
use Symfony Component Panther PantherTestCase ;
use Symfony Component Panther Client ;
class E2eTest extends PantherTestCase
{
public function testMyApp (): void
{
$ symfonyClient = static :: createClient (); // A cute kitty: Symfony's functional test tool
$ httpBrowserClient = static :: createHttpBrowserClient (); // An agile lynx: HttpBrowser
$ pantherClient = static :: createPantherClient (); // A majestic Panther
$ firefoxClient = static :: createPantherClient ([ ' browser ' => static :: FIREFOX ]); // A splendid Firefox
// Both HttpBrowser and Panther benefits from the built-in HTTP server
$ customChromeClient = Client :: createChromeClient ( null , null , [], ' https://example.com ' ); // Create a custom Chrome client
$ customFirefoxClient = Client :: createFirefoxClient ( null , null , [], ' https://example.com ' ); // Create a custom Firefox client
$ customSeleniumClient = Client :: createSeleniumClient ( ' http://127.0.0.1:4444/wd/hub ' , null , ' https://example.com ' ); // Create a custom Selenium client
// When initializing a custom client, the integrated web server IS NOT started automatically.
// Use PantherTestCase::startWebServer() or WebServerManager if you want to start it manually.
// enjoy the same API for the 3 felines
// $*client->request('GET', '...')
$ kernel = static :: createKernel (); // If you are testing a Symfony app, you also have access to the kernel
// ...
}
}
Pantherは、Mercure、Websocket、および同様のテクノロジーを使用するリアルタイム機能を備えたアプリケーションをテストする便利な方法を提供します。
PantherTestCase::createAdditionalPantherClient()
相互に対話できる追加の分離ブラウザーを作成します。たとえば、これは、複数のユーザーが同時に接続されているチャットアプリケーションをテストするのに役立ちます。
<?php
use Symfony Component Panther PantherTestCase ;
class ChatTest extends PantherTestCase
{
public function testChat (): void
{
$ client1 = self :: createPantherClient ();
$ client1 -> request ( ' GET ' , ' /chat ' );
// Connect a 2nd user using an isolated browser and say hi!
$ client2 = self :: createAdditionalPantherClient ();
$ client2 -> request ( ' GET ' , ' /chat ' );
$ client2 -> submitForm ( ' Post message ' , [ ' message ' => ' Hi folks ? ' ]);
// Wait for the message to be received by the first client
$ client1 -> waitFor ( ' .message ' );
// Symfony Assertions are always executed in the **primary** browser
$ this -> assertSelectorTextContains ( ' .message ' , ' Hi folks ? ' );
}
}
必要に応じて、Pantherを使用してコンソールのコンテンツにアクセスできます。
<?php
use Symfony Component Panther PantherTestCase ;
class ConsoleTest extends PantherTestCase
{
public function testConsole (): void
{
$ client = self :: createPantherClient (
[],
[],
[
' capabilities ' => [
' goog:loggingPrefs ' => [
' browser ' => ' ALL ' , // calls to console.* methods
' performance ' => ' ALL ' , // performance data
],
],
]
);
$ client -> request ( ' GET ' , ' / ' );
$ consoleLogs = $ client -> getWebDriver ()-> manage ()-> getLog ( ' browser ' ); // console logs
$ performanceLogs = $ client -> getWebDriver ()-> manage ()-> getLog ( ' performance ' ); // performance logs
}
}
必要に応じて、 chromedriver
バイナリに渡すように引数を構成できます。
<?php
use Symfony Component Panther PantherTestCase ;
class MyTest extends PantherTestCase
{
public function testLogging (): void
{
$ client = self :: createPantherClient (
[],
[],
[
' chromedriver_arguments ' => [
' --log-path=myfile.log ' ,
' --log-level=DEBUG '
],
]
);
$ client -> request ( ' GET ' , ' / ' );
}
}
Client::ping()
メソッドを使用して、WebDriver接続がまだアクティブであるかどうかを確認します(長期にわたるタスクに役立ちます)。
Pantherは一般的なライブラリのAPIを実装しているため、すでに広範なドキュメントがあります。
Client
クラスについては、browserkitドキュメントをお読みくださいCrawler
クラスについては、Domcrawlerのドキュメントをお読みください次の環境変数は、パンサーの動作を変更するように設定できます。
PANTHER_NO_HEADLESS
:ブラウザのヘッドレスモードを無効にするには(テストウィンドウが表示されます。デバッグに役立ちます)PANTHER_WEB_SERVER_DIR
:プロジェクトのドキュメントルートを変更するには(デフォルトに./public/
、相対パスが./
で開始する必要があります)PANTHER_WEB_SERVER_PORT
:Webサーバーのポートを変更する(デフォルトに9080
)PANTHER_WEB_SERVER_ROUTER
:各HTTPリクエストの開始時に実行されるWebサーバールータースクリプトを使用するにはPANTHER_EXTERNAL_BASE_URI
:外部Webサーバーを使用するには(PHPビルトインWebサーバーは開始されません)PANTHER_APP_ENV
:PHPアプリを実行しているWebサーバーに渡されたAPP_ENV
変数をオーバーライドするPANTHER_ERROR_SCREENSHOT_DIR
:失敗/エラースクリーンショットのベースディレクトリを設定するには(例: ./var/error-screenshots
error-screenshots)PANTHER_DEVTOOLS
:ブラウザの開発ツールを切り替えるには(デフォルトenabled
、デバッグに役立ちます)PANTHER_ERROR_SCREENSHOT_ATTACH
:上記のスクリーンショットを追加するには、junit添付ファイル形式の出力をテストするために組み込みのWebサーバーで使用されるホストおよび/またはポートを変更する場合は、 hostname
とport
createPantherClient()
メソッドの$options
パラメーターに渡します。
// ...
$ client = self :: createPantherClient ([
' hostname ' => ' example.com ' , // Defaults to 127.0.0.1
' port ' => 8080 , // Defaults to 9080
]);
PANTHER_NO_SANDBOX
:Chromeのサンドボックスを無効にするには(安全ではありませんが、コンテナでPantherを使用できます)PANTHER_CHROME_ARGUMENTS
:Chrome引数をカスタマイズする。完全にカスタマイズするには、 PANTHER_NO_HEADLESS
を設定する必要があります。PANTHER_CHROME_BINARY
:別のgoogle-chrome
バイナリを使用しますPANTHER_FIREFOX_ARGUMENTS
:Firefox引数をカスタマイズする。完全にカスタマイズするには、 PANTHER_NO_HEADLESS
を設定する必要があります。PANTHER_FIREFOX_BINARY
:別のfirefox
バイナリを使用します仕様によると、WebDriverの実装は、デフォルトで表示されたテキストのみを返します。 head
( title
など)でフィルタリングすると、メソッドtext()
空の文字列を返します。メソッドhtml()
を使用して、タグ自体を含むタグの完全な内容を取得します。
Pantherは、失敗した後、テストスイートで一時停止することができます。 Webブラウザを介して問題を調査してくれたことに本当に感謝している休憩時間です。このモードを有効にするには、ヘッドレスモードなしで--debug
phpunitオプションが必要です。
$ PANTHER_NO_HEADLESS=1 bin/phpunit --debug
Test 'AppAdminTest::testLogin' started
Error: something is wrong.
Press enter to continue...
インタラクティブモードを使用するには、phpunit拡張機能を登録する必要があります。
組み込みのPHP 1を起動する代わりに、既存のWebサーバー構成を再利用するのが便利な場合があります。これを行うには、 external_base_uri
オプションを設定します。
<?php
namespace App Tests ;
use Symfony Component Panther PantherTestCase ;
class E2eTest extends PantherTestCase
{
public function testMyApp (): void
{
$ pantherClient = static :: createPantherClient ([ ' external_base_uri ' => ' https://localhost ' ]);
// the PHP integrated web server will not be started
}
}
PHP/Symfonyアプリケーションがいくつかの異なるドメイン名を提供する可能性があります。
Pantherは、パフォーマンスを改善するためにテスト間でクライアントをメモリに保存するため、異なるドメイン名にPantherを使用していくつかのテストを作成する場合、別々のプロセスでテストを実行する必要があります。
これを行うには、ネイティブ@runInSeparateProcess
PhPunitアノテーションを使用できます。
ℹ注: Pantherが各テストでサーバーを起動して停止する必要がないため、 external_base_uri
オプションを使用して自分のWebサーバーを背景に起動するのは本当に便利です。 Symfony CLIは、迅速かつ簡単な方法です。
クライアントが使用するドメイン名を決定するために、 external_base_uri
オプションを使用した例を次に示します。
<?php
namespace App Tests ;
use Symfony Component Panther PantherTestCase ;
class FirstDomainTest extends PantherTestCase
{
/**
* @runInSeparateProcess
*/
public function testMyApp (): void
{
$ pantherClient = static :: createPantherClient ([
' external_base_uri ' => ' http://mydomain.localhost:8000 ' ,
]);
// Your tests
}
}
<?php
namespace App Tests ;
use Symfony Component Panther PantherTestCase ;
class SecondDomainTest extends PantherTestCase
{
/**
* @runInSeparateProcess
*/
public function testMyApp (): void
{
$ pantherClient = static :: createPantherClient ([
' external_base_uri ' => ' http://anotherdomain.localhost:8000 ' ,
]);
// Your tests
}
}
プロキシサーバーを使用するには、次の環境変数を設定します: PANTHER_CHROME_ARGUMENTS='--proxy-server=socks://127.0.0.1:9050'
Chromeに無効で自己署名された証明書を受け入れるように強制するには、次の環境変数を設定します。Panther_chrome_arguments PANTHER_CHROME_ARGUMENTS='--ignore-certificate-errors'
このオプションは安全ではありません。開発環境でのテストにのみ使用します。クローラー)。
Firefoxの場合、このようにクライアントをインスタンス化してください。
$ client = Client :: createFirefoxClient ( null , null , [ ' capabilities ' => [ ' acceptInsecureCerts ' => true ]]);
ここに、クロムとFirefoxの両方でPantherを実行できる最小限のDocker画像があります。
FROM php:alpine
# Chromium and ChromeDriver
ENV PANTHER_NO_SANDBOX 1
# Not mandatory, but recommended
ENV PANTHER_CHROME_ARGUMENTS= '--disable-dev-shm-usage'
RUN apk add --no-cache chromium chromium-chromedriver
# Firefox and GeckoDriver (optional)
ARG GECKODRIVER_VERSION=0.28.0
RUN apk add --no-cache firefox libzip-dev;
docker-php-ext-install zip
RUN wget -q https://github.com/mozilla/geckodriver/releases/download/v$GECKODRIVER_VERSION/geckodriver-v$GECKODRIVER_VERSION-linux64.tar.gz;
tar -zxf geckodriver-v$GECKODRIVER_VERSION-linux64.tar.gz -C /usr/bin;
rm geckodriver-v$GECKODRIVER_VERSION-linux64.tar.gz
docker build . -t myproject
run docker run -it -v "$PWD":/srv/myproject -w /srv/myproject myproject bin/phpunit
パンサーは、githubアクションで箱から出して作業します。 Pantherテストを実行するための最小限の.github/workflows/panther.yml
ファイルは次のとおりです。
name : Run Panther tests
on : [ push, pull_request ]
jobs :
tests :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v2
- name : Install dependencies
run : composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name : Run test suite
run : bin/phpunit
Pantherは、Chromeアドオンを追加すると、Travis CIで箱から出します。 Pantherテストを実行するための最小限の.travis.yml
ファイルは次のとおりです。
language : php
addons :
# If you don't use Chrome, or Firefox, remove the corresponding line
chrome : stable
firefox : latest
php :
- 8.0
script :
- bin/phpunit
GitLab CIでPantherテストを実行するための最小限の.gitlab-ci.yml
ファイルは次のとおりです。
image : ubuntu
before_script :
- apt-get update
- apt-get install software-properties-common -y
- ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
- apt-get install curl wget php php-cli php7.4 php7.4-common php7.4-curl php7.4-intl php7.4-xml php7.4-opcache php7.4-mbstring php7.4-zip libfontconfig1 fontconfig libxrender-dev libfreetype6 libxrender1 zlib1g-dev xvfb chromium-chromedriver firefox-geckodriver -y -qq
- export PANTHER_NO_SANDBOX=1
- export PANTHER_WEB_SERVER_PORT=9080
- php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
- php composer-setup.php --install-dir=/usr/local/bin --filename=composer
- php -r "unlink('composer-setup.php');"
- composer install
test :
script :
- bin/phpunit
Pantherは、Google Chromeがインストールされている限り、Appveerで箱から出します。 Pantherテストを実行するための最小限のappveyor.yml
ファイルは次のとおりです。
build : false
platform : x86
clone_folder : c:projectsmyproject
cache :
- ' %LOCALAPPDATA%Composerfiles '
install :
- ps : Set-Service wuauserv -StartupType Manual
- cinst -y php composer googlechrome chromedriver firfox selenium-gecko-driver
- refreshenv
- cd c:toolsphp80
- copy php.ini-production php.ini /Y
- echo date.timezone="UTC" >> php.ini
- echo extension_dir=ext >> php.ini
- echo extension=php_openssl.dll >> php.ini
- echo extension=php_mbstring.dll >> php.ini
- echo extension=php_curl.dll >> php.ini
- echo memory_limit=3G >> php.ini
- cd %APPVEYOR_BUILD_FOLDER%
- composer install --no-interaction --no-progress
test_script :
- cd %APPVEYOR_BUILD_FOLDER%
- php binphpunit
LiipFunctionalTestBundleなどの他のテストツールでPantherを使用したい場合、または別のベースクラスを使用する必要がある場合は、Pantherがカバーされています。 SymfonyComponentPantherPantherTestCaseTrait
を提供し、パンサーの素晴らしさで既存のテストインフラストラクチャを強化するために使用できます。
<?php
namespace App Tests Controller ;
use Liip FunctionalTestBundle Test WebTestCase ;
use Symfony Component Panther PantherTestCaseTrait ;
class DefaultControllerTest extends WebTestCase
{
use PantherTestCaseTrait ; // this is the magic. Panther is now available.
public function testWithFixtures (): void
{
$ this -> loadFixtures ([]); // load your fixtures
$ client = self :: createPantherClient (); // create your panther client
$ client -> request ( ' GET ' , ' / ' );
}
}
現在、次の機能はサポートされていません。
DOMElement
のインスタンスを返す方法(このライブラリは内部でWebDriverElement
使用しているため)プルリクエストは、残りのギャップを埋めるために歓迎します!
Bootstrap 5を使用している場合は、テストに問題がある場合があります。ブートストラップ5は、パンサーを誤解させる傾向があるスクロール効果を実装しています。
これを修正するために、ブートストラップ5 $をスタイルファイルでfalseにbaselに設定して、この効果を無効にすることをお勧めします。
$enable-smooth-scroll : false;
野生の猫種の多くは非常に脅かされています。このソフトウェアが気に入っている場合は、Panthera組織に寄付して(実際の)Panthersを保存してください。
ケビン・ダングラスによって作成されました。 Les-Tilleuls.Coopがスポンサー。
Pantherは、PHP WebDriverおよび他のいくつかのFOSSライブラリの上に構築されています。 JavaScript用のWebドライバーベースのテストツールであるNightwatch.jsに触発されました。