Um teste de navegador e biblioteca de raspagem na web para PHP e Symfony
Pantera é uma biblioteca independente conveniente para raspar sites e executar testes de ponta a ponta usando navegadores reais .
Pantera é super poderosa. Ele aproveita o protocolo Webdriver do W3C para impulsionar navegadores da Web nativos, como Google Chrome e Firefox.
A Pantera é muito fácil de usar, porque implementa as APIs populares e domiciliares da Symfony e contém todos os recursos necessários para testar seus aplicativos. Parecerá familiar se você já criou um teste funcional para um aplicativo Symfony: como a API é exatamente a mesma! Lembre -se de que a Pantera pode ser usada em todos os projetos PHP, pois é uma biblioteca independente.
A Pantera encontra automaticamente sua instalação local de Chrome ou Firefox e os inicia, para que você não precise instalar mais nada no seu computador, um servidor de selênio não é necessário!
No modo de teste, o Panther inicia automaticamente seu aplicativo usando o servidor web interno do PHP. Você pode se concentrar em escrever seus testes ou cenário de escravo na Web e a Pantera cuidará de todo o resto.
Ao contrário das bibliotecas de teste e raspagem da web que você está acostumado, Pantera:
Use o Composer para instalar o Panther em seu projeto. Você pode usar a bandeira --dev
se quiser usar apenas o Panther para testes e não para raspar a web em um ambiente de produção:
composer req symfony/panther
composer req --dev symfony/panther
A Pantera usa o protocolo Webdriver para controlar o navegador usado para rastejar sites.
Em todos os sistemas, você pode usar dbrekelmans/browser-driver-installer
para instalar o Chromedriver e o Geckodriver localmente:
composer require --dev dbrekelmans/bdi
vendor/bin/bdi detect drivers
Pantera detectará e usará drivers automaticamente armazenados no drivers/
diretório.
Como alternativa, você pode usar o gerenciador de pacotes do seu sistema operacional para instalá -los.
No Ubuntu, corra:
apt-get install chromium-chromedriver firefox-geckodriver
No Mac, usando homebrew:
brew install chromedriver geckodriver
Nas janelas, usando chocolate:
choco install chromedriver selenium-gecko-driver
Por fim, você pode baixar o Chromedriver manualmente (para cromo ou cromo) e geckodriver (para o Firefox) e colocá -los em qualquer lugar do seu PATH
ou no drivers/
diretório do seu projeto.
Se você pretende usar o Panther para testar seu aplicativo, recomendamos fortemente o registro da extensão do Panther Phpunit. Embora não seja estritamente obrigatório, essa extensão melhora drasticamente a experiência de teste, aumentando o desempenho e permitindo usar o modo interativo de depuração.
Ao usar a extensão em conjunto com a variável de ambiente PANTHER_ERROR_SCREENSHOT_DIR
, testes usando o cliente Panther que falha ou erro (depois que o cliente for criado) receberá automaticamente uma captura de tela para ajudar a depurar.
Para registrar a extensão do Panther, adicione as seguintes linhas ao phpunit.xml.dist
:
<!-- phpunit.xml.dist -->
< extensions >
< extension class = " SymfonyComponentPantherServerExtension " />
</ extensions >
Sem a extensão, o servidor da Web usado pelo Panther para servir o aplicativo em teste é iniciado sob demanda e parado quando tearDownAfterClass()
é chamado. Por outro lado, quando a extensão for registrada, o servidor da Web será interrompido somente após o último teste.
<?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!
A classe PantherTestCase
permite que você escreva testes E2E facilmente. Ele inicia automaticamente seu aplicativo usando o servidor Web PHP embutido e permite que você o rasteja usando o Panther. Para fornecer todas as ferramentas de teste com as quais você está acostumado, ele estende TestCase
da Phpunit.
Se você estiver testando um aplicativo Symfony, PantherTestCase
estende automaticamente a classe WebTestCase
. Isso significa que você pode criar facilmente testes funcionais, que podem executar diretamente o kernel do seu aplicativo e acessar todos os seus serviços existentes. Nesse caso, você pode usar todas as afirmações de teste de rastreador fornecidas pela Symfony com Panther.
<?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
}
}
Para executar este teste:
bin/phpunit tests/E2eTest.php
A Panther também oferece acesso instantâneo a outras implementações baseadas em navegação de Client
e Crawler
. Ao contrário do cliente nativo da Panther, esses clientes alternativos não oferecem suporte a JavaScript, CSS e captura de captura de tela, mas são super rápidos !
Dois clientes alternativos estão disponíveis:
WebTestCase
. É o cliente mais rápido disponível, mas está disponível apenas para aplicativos Symfony.A parte divertida é que os três clientes implementam exatamente a mesma API, para que você possa mudar de um para outro apenas chamando o método de fábrica apropriado, resultando em uma boa troca para cada caso de teste (eu preciso de JavaScript? Precisa autenticar com um servidor SSO externo?
Aqui está como recuperar instâncias desses clientes:
<?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
// ...
}
}
A Panther fornece uma maneira conveniente de testar aplicativos com recursos em tempo real que usam Mercure, WebSocket e tecnologias similares.
PantherTestCase::createAdditionalPantherClient()
cria navegadores adicionais e isolados que podem interagir entre si. Por exemplo, isso pode ser útil para testar um aplicativo de bate -papo com vários usuários conectados simultaneamente:
<?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 ? ' );
}
}
Se necessário, você pode usar o Panther para acessar o conteúdo do console:
<?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
}
}
Se necessário, você pode configurar os argumentos para passar para o Binário 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 ' , ' / ' );
}
}
Use o método Client::ping()
para verificar se a conexão WebDriver ainda está ativa (útil para tarefas de longa duração).
Como a Panther implementa a API das bibliotecas populares, ela já possui uma extensa documentação:
Client
, leia a documentação do BrowSerkitCrawler
, leia a documentação do DomcrawlerAs seguintes variáveis de ambiente podem ser configuradas para alterar o comportamento de algumas panteras:
PANTHER_NO_HEADLESS
: Para desativar o modo sem cabeça do navegador (exibirá a janela de teste, útil para depurar)PANTHER_WEB_SERVER_DIR
: para alterar a raiz do documento do projeto (padrão para ./public/
, os caminhos relativos devem começar por ./
)PANTHER_WEB_SERVER_PORT
: para alterar a porta do servidor da web (padrão para 9080
)PANTHER_WEB_SERVER_ROUTER
: para usar um script de roteador de servidor da web que é executado no início de cada solicitação HTTPPANTHER_EXTERNAL_BASE_URI
: para usar um servidor da web externo (o servidor Web interno do PHP não será iniciado)PANTHER_APP_ENV
: substituir a variável APP_ENV
passada para o servidor da web executando o aplicativo PHPPANTHER_ERROR_SCREENSHOT_DIR
: Para definir um diretório base para suas capturas de tela de falha/erro (por exemplo , ./var/error-screenshots
screenshots)PANTHER_DEVTOOLS
: para alternar as ferramentas de desenvolvimento do navegador (padrão enabled
, útil para depurar)PANTHER_ERROR_SCREENSHOT_ATTACH
: Para adicionar capturas de tela mencionadas acima para testar a saída no formato de fixação do Junit Se você deseja alterar o host e/ou a porta usada pelo servidor web interno, passe o hostname
e port
para o parâmetro $options
do método createPantherClient()
:
// ...
$ client = self :: createPantherClient ([
' hostname ' => ' example.com ' , // Defaults to 127.0.0.1
' port ' => 8080 , // Defaults to 9080
]);
PANTHER_NO_SANDBOX
: para desativar a caixa de areia do Chrome (inseguro, mas permite usar o Panther em recipientes)PANTHER_CHROME_ARGUMENTS
: para personalizar argumentos do Chrome. Você precisa definir PANTHER_NO_HEADLESS
para personalizar totalmente.PANTHER_CHROME_BINARY
: usar outro binário google-chrome
PANTHER_FIREFOX_ARGUMENTS
: para personalizar argumentos do Firefox. Você precisa definir PANTHER_NO_HEADLESS
para personalizar totalmente.PANTHER_FIREFOX_BINARY
: para usar outro binário firefox
De acordo com a especificação, as implementações do WebDriver retornam apenas o texto exibido por padrão. Quando você filtra uma etiqueta head
(como title
), o método text()
retorna uma string vazia. Use o método html()
para obter o conteúdo completo da tag, incluindo a própria tag.
Pantera pode fazer uma pausa em suas suítes de testes após uma falha. É um tempo de interrupção realmente apreciado por investigar o problema através do navegador da web. Para ativar esse modo, você precisa da opção --debug
Phpunit sem o modo sem cabeça:
$ PANTHER_NO_HEADLESS=1 bin/phpunit --debug
Test 'AppAdminTest::testLogin' started
Error: something is wrong.
Press enter to continue...
Para usar o modo interativo, a extensão Phpunit deve ser registrada.
Às vezes, é conveniente reutilizar uma configuração existente do servidor da web em vez de iniciar o PHP interno. Para fazer isso, defina a opção 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
}
}
Acontece que seu aplicativo PHP/Symfony pode servir a vários nomes de domínio diferentes.
À medida que a Panther salva o cliente na memória entre os testes para melhorar o desempenho, você precisará executar seus testes em processos separados se escrever vários testes usando o Panther para nomes de domínio diferentes.
Para fazer isso, você pode usar a anotação phpunit @runInSeparateProcess
nativa.
ℹ Nota: é realmente conveniente usar a opção external_base_uri
e iniciar seu próprio servidor da web em segundo plano, porque a Pantera não precisará iniciar e interromper seu servidor em cada teste. O Symfony CLI pode ser uma maneira rápida e fácil de fazê -lo.
Aqui está um exemplo usando a opção external_base_uri
para determinar o nome de domínio usado pelo cliente:
<?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
}
}
Para usar um servidor proxy, defina a seguinte variável de ambiente: PANTHER_CHROME_ARGUMENTS='--proxy-server=socks://127.0.0.1:9050'
Para forçar o Chrome a aceitar certificados inválidos e autoassinados, defina a seguinte variável de ambiente: PANTHER_CHROME_ARGUMENTS='--ignore-certificate-errors'
Esta opção é insegura , use-a apenas para testes em ambientes de desenvolvimento, nunca na produção (por exemplo, para a Web rastreadores).
Para o Firefox, instancie o cliente assim:
$ client = Client :: createFirefoxClient ( null , null , [ ' capabilities ' => [ ' acceptInsecureCerts ' => true ]]);
Aqui está uma imagem mínima do Docker que pode ser executada com o Pantera com Chrome e Firefox:
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
Construa -o com docker build . -t myproject
Run It com docker run -it -v "$PWD":/srv/myproject -w /srv/myproject myproject bin/phpunit
Pantera trabalha fora da caixa com ações do GitHub. Aqui está um arquivo mínimo .github/workflows/panther.yml
para executar testes do 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
A Pantera trabalhará fora da caixa com o Travis CI se você adicionar o addon Chrome. Aqui está um arquivo mínimo .travis.yml
para executar testes do 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
Aqui está um arquivo mínimo .gitlab-ci.yml
para executar testes do Panther com Gitlab CI:
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
A Pantera funcionará fora da caixa com a AppVeyor enquanto o Google Chrome estiver instalado. Aqui está um arquivo mínimo appveyor.yml
para executar testes do 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
Se você deseja usar o Panther com outras ferramentas de teste como o liipfunctionaltestbundle ou se precisar usar uma classe base diferente, o Panther o abordará. Ele fornece o SymfonyComponentPantherPantherTestCaseTrait
e você pode usá-lo para aprimorar sua infraestrutura de teste existente com alguma grandiosidade do Panther:
<?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 ' , ' / ' );
}
}
Os seguintes recursos não são suportados no momento:
DOMElement
(porque esta biblioteca usa WebDriverElement
internamente)Solicitações de tração são bem -vindas para preencher as lacunas restantes!
Se você estiver usando o Bootstrap 5, poderá ter um problema com o teste. Bootstrap 5 implementa um efeito de rolagem, que tende a enganar a Pantera.
Para corrigir isso, aconselhamos você a desativar esse efeito, definindo a variável Bootstrap 5 $ Atable-Smooth-scroll como false em seu arquivo de estilo.
$enable-smooth-scroll : false;
Muitas das espécies de gatos selvagens são altamente ameaçadas. Se você gosta deste software, ajude a salvar os Panteras (reais) doando para a organização Panthera.
Criado por Kévin Dunglas. Patrocinado por les-tilleuls.coop.
A Pantera é construída sobre o PHP Webdriver e várias outras bibliotecas de fossos. Foi inspirado no Nightwatch.js, uma ferramenta de teste baseada em Webdriver para JavaScript.