PHP和Symfony的瀏覽器測試和網絡刮擦庫
Panther是一個方便的獨立庫,用於刮擦網站並使用真實瀏覽器運行端到端測試。
Panther超級強大。它利用W3C的Web驅動器協議來驅動本機Web瀏覽器,例如Google Chrome和Firefox。
Panther非常易於使用,因為它實現了Symfony流行的Browserkit和Domcrawler API,並且包含您測試應用程序所需的所有功能。如果您曾經為Symfony應用程序創建了功能測試,這聽起來很熟悉:因為API完全相同!請記住,Panther可以在每個PHP項目中使用,因為它是一個獨立的庫。
Panther會自動找到您的本地安裝Chrome或Firefox並啟動它們,因此您無需在計算機上安裝其他任何內容,不需要硒服務器!
在測試模式下,Panther會使用PHP內置的Web-Server自動啟動您的應用程序。您可以專注於編寫測試或網絡剪裁方案,而Panther將負責其他所有內容。
與測試和網絡刮擦庫不同,Panther:
使用作曲家在項目中安裝Panther。如果要使用Panther僅測試而不是在生產環境中進行網絡刮擦,則可能需要使用--dev
標誌:
composer req symfony/panther
composer req --dev symfony/panther
Panther使用WebDriver協議來控制用於爬網網站的瀏覽器。
在所有系統上,您都可以使用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上使用自製:
brew install chromedriver geckodriver
在窗戶上,使用巧克力:
choco install chromedriver selenium-gecko-driver
最後,您可以下載手動Chromedriver(用於Chromium或Chrome)和Geckodriver(對於Firefox),並將它們放入您的PATH
或項目的drivers/
目錄中。
如果您打算使用Panther測試您的應用程序,我們強烈建議註冊Panther Phpunit擴展程序。儘管不是嚴格強制性的,但這種擴展可以通過提高性能並允許使用交互式調試模式來大大改善測試經驗。
與PANTHER_ERROR_SCREENSHOT_DIR
環境變量一起使用擴展程序時,使用Panther客戶端的測試失敗或錯誤(創建客戶端后)將自動獲取屏幕截圖以幫助調試。
要註冊Panther Extension,請將以下行添加到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和屏幕截圖捕獲,但它們非常快!
有兩個替代客戶可用:
WebTestCase
提供的符號內核。它是最快的客戶端,但僅適用於Symfony應用程序。有趣的部分是3個客戶實現了完全相同的API,因此您可以通過調用適當的工廠方法來從一個端子轉到另一個API,從而為每個測試用例提供了良好的權衡(我需要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
:要更改項目的文檔root(默認為./public/
,相對路徑必須從./
開始)PANTHER_WEB_SERVER_PORT
:更改Web服務器的端口(默認為9080
)PANTHER_WEB_SERVER_ROUTER
:使用在每個HTTP請求開始時運行的Web服務器路由器腳本PANTHER_EXTERNAL_BASE_URI
:使用外部Web服務器(將不會啟動PHP內置的Web服務器)PANTHER_APP_ENV
:覆蓋APP_ENV
變量傳遞給運行PHP應用程序的Web服務器PANTHER_ERROR_SCREENSHOT_DIR
:為失敗/錯誤屏幕./var/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
Tag上過濾(如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擴展名。
有時,重複使用現有的Web服務器配置而不是啟動內置PHP ONE是很方便的。為此,設置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註釋。
ℹ注意:使用external_base_uri
選項並在後台啟動自己的Web服務器確實很方便,因為Panther不必在每個測試中啟動和停止服務器。 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='--ignore-certificate-errors'
此選項是不安全的,僅在開發環境中進行測試,從不在生產中(例如爬蟲)。
對於Firefox,這樣就可以實例化客戶:
$ client = Client :: createFirefoxClient ( null , null , [ ' capabilities ' => [ ' acceptInsecureCerts ' => true ]]);
這是一個最小的Docker圖像,可以與Chrome和Firefox一起運行Panther:
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
使用docker run -it -v "$PWD":/srv/myproject -w /srv/myproject myproject bin/phpunit
Panther用GitHub動作開箱即用。這是一個最小的.github/workflows/panther.yml
文件,用於運行Panther測試:
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
如果您添加Chrome插件,Panther將使用Travis CI開箱即用。這是一個最小的.travis.yml
文件,用於運行Panther測試:
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.yml
文件,用於使用Gitlab CI運行Panther測試:
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
只要安裝Google Chrome,Panther就會與Appveyor開箱即用。這是一個最小的appveyor.yml
文件,用於運行Panther測試:
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
如果您想將Panther用於其他測試工具,例如LiipfunctionalTestBundle,或者只需使用其他基類,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,則可能在測試中遇到問題。 Bootstrap 5實現了滾動效果,這往往會誤導Panther。
為了解決此問題,我們建議您通過將Bootstrap 5 $ enable-smooth-scroll變量設置為“ false”中的false,以在樣式文件中停用這種效果。
$enable-smooth-scroll : false;
許多野貓物種受到高度威脅。如果您喜歡此軟件,請通過向Panthera組織捐款來幫助保存(真實的)黑豹。
由KévinDunglas創建。由les-tilleuls.coop贊助。
Panther建在PHP Webdriver和其他幾個FOSS庫的頂部。它的啟發是由JavaScript的基於網絡驅動器的測試工具NightWatch.js的啟發。