端到端测试
Panther 组件允许使用 PHP 驱动真实的 Web 浏览器来创建端到端测试。
安装
1
$ composer require symfony/panther
注意
如果你在 Symfony 应用外部安装此组件,你必须在你的代码中引入 vendor/autoload.php
文件,以启用 Composer 提供的类自动加载机制。阅读 这篇文章 以获取更多细节。
简介
端到端测试是一种特殊的应用测试类型,它模拟真实用户与你的应用进行交互。它们通常用于测试你的应用的用户界面(UI)以及这些交互的效果(例如,当我点击此按钮时,必须发送一封邮件)。与上面详述的功能测试不同的是,端到端测试使用真实的浏览器而不是模拟的浏览器。此浏览器可以在无头模式(没有图形界面)下运行,也可以不在无头模式下运行。第一种选择方便在持续集成(CI)中运行测试,而第二种选择对于调试目的很有用。
这就是 Panther 的目的,Panther 是一个提供真实浏览器来运行你的测试的组件。以下是使 Panther 与 Symfony 提供的其他测试工具相比显得特别的一些方面
- 可以在测试期间的任何时间点截取浏览器屏幕截图
- 网页中包含的 JavaScript 代码会被执行
- Panther 支持 Chrome (或 Firefox) 实现的所有功能
- 测试实时应用(例如,使用 Mercure 的 WebSockets、服务器发送事件等)的便捷方式
安装 Web 驱动
Panther 使用 WebDriver 协议来控制用于抓取网站的浏览器。在所有系统上,你可以使用 dbrekelmans/browser-driver-installer 在本地安装 ChromeDriver 和 geckodriver
1 2 3
$ composer require --dev dbrekelmans/bdi
$ vendor/bin/bdi detect drivers
Panther 将检测并自动使用存储在你的项目的 drivers/
目录中的驱动程序(如果手动安装它们)。你可以下载 Chromium 或 Chrome 的 ChromeDriver 和 Firefox 的 GeckoDriver,并将它们放在你的 PATH
中的任何位置或你的项目的 drivers/
目录中。
或者,你可以使用你的操作系统的包管理器来安装它们
1 2 3 4 5 6 7 8
# Ubuntu
$ apt-get install chromium-chromedriver firefox-geckodriver
# MacOS, using Homebrew
$ brew install chromedriver geckodriver
# Windows, using Chocolatey
$ choco install chromedriver selenium-gecko-driver
注册 PHPUnit 扩展
如果你打算使用 Panther 来测试你的应用,强烈建议注册 Panther PHPUnit 扩展。虽然不是强制性的,但此扩展通过提高性能并允许使用 交互式调试模式,极大地改善了测试体验。
当结合 PANTHER_ERROR_SCREENSHOT_DIR
环境变量使用扩展时,使用 Panther 客户端的失败或错误测试(在客户端创建之后)将自动截取屏幕截图以帮助调试。
要注册 Panther 扩展,请将以下行添加到 phpunit.xml.dist
1 2 3 4
<!-- phpunit.xml.dist -->
<extensions>
<extension class="Symfony\Component\Panther\ServerExtension"/>
</extensions>
如果没有扩展,Panther 用于为被测应用提供服务的 Web 服务器将按需启动,并在调用 tearDownAfterClass()
时停止。另一方面,当注册扩展时,Web 服务器将仅在最后一个测试之后停止。
用法
这是一个使用 Panther 测试应用的片段示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
use Symfony\Component\Panther\Client;
$client = Client::createChromeClient();
// alternatively, create a Firefox client
$client = Client::createFirefoxClient();
$client->request('GET', 'https://api-platform.com');
$client->clickLink('Getting started');
// wait for an element to be present in the DOM, even if hidden
$crawler = $client->waitFor('#bootstrapping-the-core-library');
// you can also wait for an element to be visible
$crawler = $client->waitForVisibility('#bootstrapping-the-core-library');
// get the text of an element thanks to the query selector syntax
echo $crawler->filter('div:has(> #bootstrapping-the-core-library)')->text();
// take a screenshot of the current page
$client->takeScreenshot('screen.png');
注意
根据规范,WebDriver 实现默认情况下仅返回显示的文本。当你在 head
标签(如 title
)上进行过滤时,text()
方法返回空字符串。使用 html()
方法获取标签的完整内容,包括标签本身。
创建一个 TestCase
PantherTestCase
类允许你编写端到端测试。它使用内置的 PHP Web 服务器自动启动你的应用,并让你使用 Panther 抓取它。为了提供你习惯的所有测试工具,它扩展了 PHPUnit 的 TestCase
。
如果你正在测试 Symfony 应用,PantherTestCase
会自动扩展 WebTestCase 类。这意味着你可以创建功能测试,它可以直接执行你的应用的内核并访问你所有现有的服务。在这种情况下,你可以将 Symfony 提供的所有爬虫测试断言与 Panther 一起使用。
这是一个 PantherTestCase
的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
namespace App\Tests;
use Symfony\Component\Panther\PantherTestCase;
class HomepageTest extends PantherTestCase
{
public function testMyApp(): void
{
// your app is automatically started using the built-in web server
$client = static::createPantherClient();
$client->request('GET', '/home');
// 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');
// ...
}
}
Panther 客户端带有等待某些异步进程完成的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
namespace App\Tests;
use Symfony\Component\Panther\PantherTestCase;
class HomepageTest extends PantherTestCase
{
public function testMyApp(): void
{
// ...
// wait for element to be attached to the DOM
$client->waitFor('.popin');
// wait for element to be removed from the DOM
$client->waitForStaleness('.popin');
// wait for element of the DOM to become visible
$client->waitForVisibility('.loader');
// wait for element of the DOM to become hidden
$client->waitForInvisibility('.loader');
// wait for text to be inserted in the element content
$client->waitForElementToContain('.total', '25 €');
// wait for text to be removed from the element content
$client->waitForElementToNotContain('.promotion', '5%');
// wait for the button to become enabled
$client->waitForEnabled('[type="submit"]');
// wait for the button to become disabled
$client->waitForDisabled('[type="submit"]');
// wait for the attribute to contain content
$client->waitForAttributeToContain('.price', 'data-old-price', '25 €');
// wait for the attribute to not contain content
$client->waitForAttributeToNotContain('.price', 'data-old-price', '25 €');
}
}
最后,你还可以对将来会发生的事情进行断言
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
namespace App\Tests;
use Symfony\Component\Panther\PantherTestCase;
class HomepageTest extends PantherTestCase
{
public function testMyApp(): void
{
// ...
// element will be attached to the DOM
$this->assertSelectorWillExist('.popin');
// element will be removed from the DOM
$this->assertSelectorWillNotExist('.popin');
// element will be visible
$this->assertSelectorWillBeVisible('.loader');
// element will not be visible
$this->assertSelectorWillNotBeVisible('.loader');
// text will be inserted in the element content
$this->assertSelectorWillContain('.total', '€25');
// text will be removed from the element content
$this->assertSelectorWillNotContain('.promotion', '5%');
// button will be enabled
$this->assertSelectorWillBeEnabled('[type="submit"]');
// button will be disabled
$this->assertSelectorWillBeDisabled('[type="submit"]');
// attribute will contain content
$this->assertSelectorAttributeWillContain('.price', 'data-old-price', '€25');
// attribute will not contain content
$this->assertSelectorAttributeWillNotContain('.price', 'data-old-price', '€25');
}
}
然后,你可以使用 PHPUnit 运行此测试,就像你对任何其他测试一样
1
$ ./vendor/bin/phpunit tests/HomepageTest.php
在编写端到端测试时,你应该记住它们比其他测试慢。如果你需要检查 WebDriver 连接在长时间运行的测试期间是否仍然处于活动状态,你可以使用 Client::ping()
方法,该方法根据连接状态返回布尔值。
高级用法
更改 Web 服务器的主机名和端口
如果你想更改内置 Web 服务器使用的主机和/或端口,请将 hostname
和 port
传递给 createPantherClient()
方法的 $options
参数
1 2 3 4
$client = self::createPantherClient([
'hostname' => 'example.com', // defaults to 127.0.0.1
'port' => 8080, // defaults to 9080
]);
使用 Browser-Kit 客户端
Panther 还允许访问其他基于 BrowserKit 的 Client
和 Crawler
实现。与 Panther 的原生客户端不同,这些替代客户端不支持 JavaScript、CSS 和屏幕截图捕获,但速度更快。有两个替代客户端可用
- 第一个直接操作
WebTestCase
提供的 Symfony 内核。它是最快的可用客户端,但仅适用于 Symfony 应用。 - 第二个利用 HttpBrowser。它是 Symfony 内核和 Panther 测试客户端之间的中间层。
HttpBrowser
使用 HttpClient 组件 发送真实的 HTTP 请求。它速度很快,可以浏览任何网页,而不仅仅是被测应用的网页。但是,HttpBrowser 不支持 JavaScript 和其他高级功能,因为它完全用 PHP 编写。这个客户端可以在任何 PHP 应用中使用。
由于所有客户端都实现了相同的 API,你可以通过调用适当的工厂方法从一个客户端切换到另一个客户端,从而为每个测试用例带来良好的权衡:是否需要 JavaScript,是否需要针对外部 SSO 进行身份验证等等。
以下是如何检索这些客户端的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
namespace App\Tests;
use Symfony\Component\Panther\Client;
use Symfony\Component\Panther\PantherTestCase;
class AppTest extends PantherTestCase
{
public function testMyApp(): void
{
// retrieve an existing client
$symfonyClient = static::createClient();
$httpBrowserClient = static::createHttpBrowserClient();
$pantherClient = static::createPantherClient();
$firefoxClient = static::createPantherClient(['browser' => static::FIREFOX]);
// create a custom client
$customChromeClient = Client::createChromeClient(null, null, [], 'https://example.com');
$customFirefoxClient = Client::createFirefoxClient(null, null, [], 'https://example.com');
$customSeleniumClient = Client::createSeleniumClient('http://127.0.0.1:4444/wd/hub', null, 'https://example.com');
// if you are testing a Symfony app, you also have access to the kernel
$kernel = static::createKernel();
// ...
}
}
注意
当初始化自定义客户端时,集成的 Web 服务器不会自动启动。如果你想手动启动它,请使用 PantherTestCase::startWebServer()
或 WebServerManager
类。
测试实时应用
Panther 提供了一种便捷的方式来测试具有实时能力的应用,这些应用使用 Mercure、WebSocket 和类似技术。
PantherTestCase::createAdditionalPantherClient()
方法可以创建额外的、隔离的浏览器,这些浏览器可以与其他浏览器进行交互。例如,这对于测试具有多个用户同时连接的聊天应用非常有用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
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
$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 访问控制台的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
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');
$performanceLogs = $client->getWebDriver()->manage()->getLog('performance'); // performance logs
}
}
传递参数给 ChromeDriver
如果需要,你可以配置要传递给 chromedriver
二进制文件的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
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', '/');
}
}
使用代理
要使用代理服务器,你必须设置 PANTHER_CHROME_ARGUMENTS
1 2
# .env.test
PANTHER_CHROME_ARGUMENTS='--proxy-server=socks://127.0.0.1:9050'
将 Selenium 与内置 Web 服务器一起使用
如果你想将 Selenium Grid 与内置 Web 服务器一起使用,你需要按如下方式配置 Panther 客户端
1 2 3 4 5 6 7 8 9
$client = Client::createPantherClient(
options: [
'browser' => PantherTestCase::SELENIUM,
],
managerOptions: [
'host' => 'http://selenium-hub:4444', // the host of the Selenium Server (Grid)
'capabilities' => DesiredCapabilities::firefox(), // the capabilities of the browser
],
);
接受自签名 SSL 证书
要强制 Chrome 接受无效和自签名证书,你可以设置以下环境变量:PANTHER_CHROME_ARGUMENTS='--ignore-certificate-errors'
。
危险
此选项不安全,仅在开发环境中进行测试时使用,切勿在生产环境中使用(例如,对于 Web 爬虫)。
对于 Firefox,像这样实例化客户端,你可以在客户端创建时执行此操作
1
$client = Client::createFirefoxClient(null, null, ['capabilities' => ['acceptInsecureCerts' => true]]);
使用外部 Web 服务器
有时,重用现有的 Web 服务器配置而不是启动内置 PHP 服务器会更方便。为此,在创建客户端时设置 external_base_uri
选项
1 2 3 4 5 6 7 8 9 10 11 12 13
namespace App\Tests;
use Symfony\Component\Panther\PantherTestCase;
class E2eTest extends PantherTestCase
{
public function testMyApp(): void
{
$pantherClient = static::createPantherClient(['external_base_uri' => 'http://127.0.0.1']);
// ...
}
}
注意
当使用外部 Web 服务器时,Panther 将不会启动内置 PHP Web 服务器。
拥有一个多域名应用
你的 PHP/Symfony 应用可能服务于多个不同的域名。由于 Panther 在测试之间将客户端保存在内存中以提高性能,如果你编写多个使用 Panther 进行不同域名测试的测试,则必须在单独的进程中运行测试。
为此,你可以使用原生的 @runInSeparateProcess
PHPUnit 注解。这是一个使用 external_base_uri
选项来确定客户端在使用单独进程时使用的域名的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
// tests/FirstDomainTest.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',
]);
// ...
}
}
// tests/SecondDomainTest.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',
]);
// ...
}
}
与其他测试工具一起使用
如果你想将 Panther 与其他测试工具(如 LiipFunctionalTestBundle)一起使用,或者你只需要使用不同的基类,则可以使用 Symfony\Component\Panther\PantherTestCaseTrait
来增强你现有的测试基础设施,使其具备一些 Panther 机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
namespace App\Tests\Controller;
use Liip\FunctionalTestBundle\Test\WebTestCase;
use Symfony\Component\Panther\PantherTestCaseTrait;
class DefaultControllerTest extends WebTestCase
{
use PantherTestCaseTrait;
public function testWithFixtures(): void
{
$this->loadFixtures([]); // load your fixtures
$client = self::createPantherClient(); // create your panther client
$client->request('GET', '/');
// ...
}
}
通过环境变量配置 Panther
可以设置以下环境变量来更改 Panther 的某些行为
PANTHER_NO_HEADLESS
- 禁用浏览器的无头模式(将显示测试窗口,有助于调试)
PANTHER_WEB_SERVER_DIR
- 更改项目的文档根目录(默认为
./public/
,相对路径必须以./
开头) PANTHER_WEB_SERVER_PORT
- 更改 Web 服务器的端口(默认为
9080
) PANTHER_WEB_SERVER_ROUTER
- 使用 Web 服务器路由器脚本,该脚本在每个 HTTP 请求开始时运行
PANTHER_EXTERNAL_BASE_URI
- 使用外部 Web 服务器(不会启动 PHP 内置 Web 服务器)
PANTHER_APP_ENV
- 覆盖传递给运行 PHP 应用的 Web 服务器的
APP_ENV
变量 PANTHER_ERROR_SCREENSHOT_DIR
- 为你的失败/错误屏幕截图设置一个基本目录(例如
./var/error-screenshots
) PANTHER_DEVTOOLS
- 切换浏览器的开发者工具(默认为
enabled
,有助于调试) PANTHER_ERROR_SCREENSHOT_ATTACH
- 将上面提到的屏幕截图以 junit 附件格式添加到测试输出中
PANTHER_NO_REDUCED_MOTION
- 禁用浏览器中非必要的移动(例如动画)
2.2.0
对 PANTHER_NO_REDUCED_MOTION
环境变量的支持在 Panther 2.2.0 中添加。
Chrome 特定的环境变量
PANTHER_NO_SANDBOX
- 禁用 Chrome 的沙箱(不安全,但允许在容器中使用 Panther)
PANTHER_CHROME_ARGUMENTS
- 自定义 Chrome 参数。你需要将
PANTHER_NO_HEADLESS
设置为1
才能完全自定义 PANTHER_CHROME_BINARY
- 使用另一个
google-chrome
二进制文件
Firefox 特定的环境变量
PANTHER_FIREFOX_ARGUMENTS
- 自定义 Firefox 参数。你需要设置
PANTHER_NO_HEADLESS
才能完全自定义 PANTHER_FIREFOX_BINARY
- 使用另一个
firefox
二进制文件
更改浏览器窗口的大小
可以控制浏览器窗口的大小。这也控制了屏幕截图的大小。
这是你在 Chrome 中执行此操作的方式
1
$client = Client::createChromeClient(null, ['--window-size=1500,4000']);
你可以通过将 PANTHER_CHROME_ARGUMENTS
环境变量设置为 --window-size=1500,4000
来实现相同的效果。
在 Firefox 上,这是你执行此操作的方式
1 2 3 4 5
use Facebook\WebDriver\WebDriverDimension;
$client = Client::createFirefoxClient();
$size = new WebDriverDimension(1500, 4000);
$client->manage()->window()->setSize($size);
交互模式
Panther 可以在测试套件失败后暂停。借助这段暂停时间,您可以通过 Web 浏览器调查遇到的问题。要启用此模式,您需要使用不带 headless 模式的 --debug
PHPUnit 选项
1 2 3 4 5 6
$ PANTHER_NO_HEADLESS=1 bin/phpunit --debug
Test 'App\AdminTest::testLogin' started
Error: something is wrong.
Press enter to continue...
要使用交互模式,必须注册 PHPUnit 扩展。
Docker 集成
这是一个最小的 Docker 镜像,可以运行带有 Chrome 和 Firefox 的 Panther
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
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
然后您可以构建并运行您的镜像
1 2
$ docker build . -t myproject
$ docker run -it -v "$PWD":/srv/myproject -w /srv/myproject myproject bin/phpunit
在你的 CI 中集成 Panther
Github Actions
Panther 可以与 GitHub Actions 开箱即用。这是一个最小的 .github/workflows/panther.yaml
文件,用于运行 Panther 测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
name: Run Panther tests
on: [ push, pull_request ]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: "ramsey/composer-install@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
Travis CI
如果您添加 Chrome 插件,Panther 将可以与 Travis CI 开箱即用。这是一个最小的 .travis.yaml
文件,用于运行 Panther 测试
1 2 3 4 5 6 7 8 9 10 11
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
这是一个最小的 .gitlab-ci.yaml
文件,用于通过 Gitlab CI 运行 Panther 测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
image: ubuntu
before_script:
- apt-get update
- apt-get install software-properties-common -y
- ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime
- apt-get install curl wget php php-cli php8.1 php8.1-common php8.1-curl php8.1-intl php8.1-xml php8.1-opcache php8.1-mbstring php8.1-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('http://getcomposer.org.cn/installer', 'composer-setup.php');"
- php composer-setup.php --install-dir=/usr/local/bin --filename=composer
- php -r "unlink('composer-setup.php');"
- composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
test:
script:
- bin/phpunit
AppVeyor
只要安装了 Google Chrome,Panther 就可以与 AppVeyor 开箱即用。这是一个最小的 appveyor.yaml
文件,用于运行 Panther 测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
build: false
platform: x86
clone_folder: c:\projects\myproject
cache:
- '%LOCALAPPDATA%\Composer\files'
install:
- ps: Set-Service wuauserv -StartupType Manual
- cinst -y php composer googlechrome chromedriver firfox selenium-gecko-driver
- refreshenv
- cd c:\tools\php80
- 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 -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
test_script:
- cd %APPVEYOR_BUILD_FOLDER%
- php bin\phpunit
已知限制和故障排除
目前不支持以下功能
- 抓取 XML 文档(仅支持 HTML)
- 更新现有文档(浏览器主要用于消费数据,而不是创建网页)
- 使用多维 PHP 数组语法设置表单值
- 返回
\DOMElement
实例的方法(因为此库在内部使用WebDriverElement
) - 在 select 中选择无效选项
此外,如果您正在使用 Bootstrap 5,则存在已知问题。它实现了一种滚动效果,这种效果容易误导 Panther。为了解决这个问题,我们建议您通过在样式文件中将 Bootstrap 5 的 $enable-smooth-scroll
变量设置为 false
来禁用此效果
1
$enable-smooth-scroll: false;
使用 PHP 内置服务器时资源未加载
有时,您的资源可能在测试期间无法加载。这是因为 Panther 使用 PHP 内置服务器 来服务您的应用程序。如果资源文件(或任何请求的 URI,不是 .php
文件)不在您的 public 目录中,内置服务器将返回 404 错误。当您让 AssetMapper 组件 在 dev
环境中处理您的应用程序资源时,通常会发生这种情况。
使用 AssetMapper 时,一种解决方案是在运行测试之前 编译资源。这也将加快您的测试速度,因为 Symfony 不需要处理资源,从而允许 PHP 内置服务器直接服务它们。
另一种选择是创建一个名为 tests/router.php
的文件,并将以下内容添加到其中
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// tests/router.php
if (is_file($_SERVER['DOCUMENT_ROOT'].\DIRECTORY_SEPARATOR.$_SERVER['SCRIPT_NAME'])) {
return false;
}
$script = 'index.php';
$_SERVER = array_merge($_SERVER, $_ENV);
$_SERVER['SCRIPT_FILENAME'] = $_SERVER['DOCUMENT_ROOT'].\DIRECTORY_SEPARATOR.$script;
$_SERVER['SCRIPT_NAME'] = \DIRECTORY_SEPARATOR.$script;
$_SERVER['PHP_SELF'] = \DIRECTORY_SEPARATOR.$script;
require $script;
然后在 phpunit.xml.dist
中使用 PANTHER_WEB_SERVER_ROUTER
环境变量将其声明为 Panther 服务器的路由器
1 2 3 4 5 6 7 8
<!-- phpunit.xml.dist -->
<phpunit>
<!-- ... -->
<php>
<!-- ... -->
<server name="PANTHER_WEB_SERVER_ROUTER" value="./tests/router.php"/>
</php>
</phpunit>
另请参阅
请参阅 SymfonyCasts 上的 功能测试教程。
附加文档
由于 Panther 实现了流行库的 API,您可以找到更多文档
- 对于
Client
类,请阅读 BrowserKit 组件 页面 - 对于
Crawler
类,请阅读 DomCrawler 组件 页面 - 对于 WebDriver,请阅读 PHP WebDriver 文档