modera / selenium-tools
包含运行 Selenium 测试的工具 - 多会话测试、录制视频等
Requires
- php: >=5.5.9
- behat/behat: ^3.0
- behat/mink: ^1.7
- doctrine/common: ^2.7
- facebook/webdriver: ~1.3
- guzzlehttp/guzzle: ~6.2
- phake/phake: ~2.3
- phpunit/phpunit: ~3.0|~4.0|~5.0
This package is auto-updated.
Last update: 2024-09-14 23:50:04 UTC
README
本软件包包含了一组组件,您可能在以下情况下想要使用它们:
- 您想编写多用户测试 - 涉及多个用户(和浏览器)之间协作的测试用例
- 您想在无头环境中运行测试(如 CI),并能录制测试执行的视频
- 您想使用 Behat 编写多用户 BDD 测试
- 您想测试基于 ExtJs/MJR 的应用程序,并避免低级 DOM 操作
编写多用户测试
有时,仅使用单个用户的端到端(E2E)测试并不足以解决问题。例如,为了测试聊天应用程序是否真的能工作,您需要模拟两个用户之间的协作 - 一个可能是发起对话的前端用户和一个回复前端用户问题的管理员。在这种情况下,我们有两个角色 - 管理员和客户,因此我们的测试场景可能如下所示
- 客户通过发送“你好,我有一个问题”这样的消息来启动对话
- 管理员应接收消息 - 我们可以定期检查页面源代码中是否包含“你好,我有一个问题”的文本片段
- 一旦管理员收到消息,他将会回复 - “你好,我能帮你什么?”并发送回复
- 现在我们检查客户的页面是否包含文本片段“你好,我能帮你什么?”
虽然我们的测试场景非常简单,在这种情况下只包含4个步骤,但足以确保我们的聊天应用程序的基本功能按预期工作。在这个库的上下文中,为了模拟用户,我们使用了一个称为“演员”的高级抽象。本质上,“演员”代表一个由 Selenium 管理的独立浏览器,并添加了一些有用的功能
- 您不需要手动初始化和管理 Selenium 会话,只需将 URL 传递给演员即可。如果您仍然希望更好地控制底层发生的事情,我们也有相应的解决方案
- 当您有多个演员(即浏览器)并在它们之间切换执行某些操作时,演员(通过 TestHarness,我们将在下文中提到)会自动将浏览器置于最前(聚焦)。是的,Selenium 不会自动为您做到这一点。
- 您可以通过明确的方式在不同演员之间传递数据。例如,在上述示例测试场景中,我们可以从“客户”演员传递一个消息给“管理员”演员,该消息是管理员预期在页面源代码中出现的 - “你好,我有一个问题”。
为了有效地协调不同演员之间的协作并使它们之间传递数据成为可能,它们应附加到 TestHarness,后者定义了您想使用的演员,并使得在它们之间切换变得容易。
俗话说“一图胜千言”,让我们遵循这个古老的谚语,通过实现一个简单的测试用例来探索提供的 API,测试聊天应用程序是否正确工作。
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\WebDriverCapabilityType;
use Facebook\WebDriver\WebDriverKeys;
use Modera\Component\SeleniumTools\Actor;
use Modera\Component\SeleniumTools\Querying\By;
use Modera\Component\SeleniumTools\TestHarness;
class AppearInTest extends \PHPUnit_Framework_TestCase
{
/**
* @var TestHarness
*/
private $harness;
public function setUp()
{
$roomUrl = 'https://appear.in/seleniumtools-'.\uniqid();
// 1
$driverFactory = new CallbackDriverFactory(function() {
$options = new ChromeOptions();
$options->addArguments([
'use-fake-ui-for-media-stream',
]);
$capabilities = DesiredCapabilities::chrome();
$capabilities->setCapability(ChromeOptions::CAPABILITY, $options);
$driver = RemoteWebDriver::create(
'https://:4444/wd/hub', $capabilities
);
return $driver;
});
$this->harness = new TestHarness('default', null, $driverFactory);
$this->harness->addActor('customer', $roomUrl);
$this->harness->addActor('admin', $roomUrl);
}
public function tearDown()
{
$this->harness->halt();
}
public function testBasicMessageExchange()
{
$this->harness->runAs('admin', function() {}); // 2
$this->harness->runAs('customer', function(RemoteWebDriver $driver, Actor $actor) { // 3
$message = 'Hello from customer '.uniqid();
$driver->findElement(By::id('chat-button'))->click();
sleep(1);
$inputField = $driver->findElement(By::name('message'));
$inputField->sendKeys([
$message,
WebDriverKeys::ENTER
]);
sleep(1);
$actor->getHarness()->setContextValue('customer_message', $message); // 4
});
$this->harness->runAs('admin', function(RemoteWebDriver $driver, Actor $actor) { // 5
$this->assertContains(
$actor->getHarness()->getContextValue('customer_message'), // 6
$driver->getPageSource()
);
});
}
}
我必须承认,上面的案例在某些方面可以被视为一个高级案例,因为在这种情况下,我们需要启用虚假的媒体流,这样Chrome浏览器就不会向用户请求确认以允许访问其麦克风。如果您想了解如何运行此测试案例,请向下滚动一点。到目前为止,我们将更详细地讨论每个在注释中指定的时刻的作用。
- 如我之前提到的,这是一个相当高级的示例,因此为了禁用麦克风,我们需要更好地控制Selenium驱动程序是如何创建的(记住,在本文档的开头提到过,如果您需要进行一些调整,您始终可以这样做),为了实现这一点,我们定义了一个自定义的驱动程序工厂,它告诉Chrome(此测试场景假定您已在本地上安装Chrome)模拟麦克风。大多数情况下,您不需要进行这种高级配置,但仍然,当您需要时,拥有这个选项总是很好的。
- 在这里,我们打开一个“admin”浏览器,这是为了解决出现.in不会显示在浏览器打开之前发送的消息的事实,因此通过打开一个“admin”浏览器,我们确保当“customer”实际发送消息时,我们会收到。
- 在这个步骤中,我们找到打开聊天区域并发送消息的按钮。
- 在这里,我们缓存了一个已发送的消息,稍后在“admin”演员中,我们将提取这个值并验证页面的HTML源代码是否包含这段文本。
- 重新激活“admin”演员。浏览器只为每个客户打开一次(除非您明确将其终止),因此在这种情况下,我们在步骤“1”中打开的浏览器将只接收焦点。
- 在这里,我们取“customer”演员发送的消息,并验证浏览器页面的源代码确实包含指定的文本。
为了运行此测试案例,您需要按照以下步骤操作
-
将
modera/selenium-tools
和phpunit/phpunit
作为包添加到您的composer.json中,并运行composer update
。以下是一个示例composer.json{ "name": "acme/testing-selenium", "type": "project", "license": "MIT", "prefer-stable": true, "minimum-stability": "dev", "require": { "modera/selenium-tools": "dev-master", "phpunit/phpunit": "^5.0" } }
-
创建一个
phpunit.xml.dist
,它可能看起来像这样<?xml version="1.0" encoding="UTF-8"?> <phpunit backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" syntaxCheck="false" bootstrap="vendor/autoload.php" > <testsuites> <testsuite name="Sample testcase"> <directory>./tests/</directory> </testsuite> </testsuites> </phpunit>
-
将测试放在PHPUnit可以找到的文件中。如果您使用列表中的步骤2的文件,则该文件必须放置在
tests
目录中。 -
下载Selenium
-
下载一个chrome driver并将其放置在下载Selenium jar文件旁边。
-
运行
java -jar downloaded-selenium.jar
,其中downloaded-selenium.jar
是您在步骤4中下载的Selenium的名称。 -
保持Selenium运行并运行
./vendor/bin/phpunit
。如果您已安装Docker,则可以像这样运行它 -docker run --network=host -it --rm -v $(pwd):/mnt/tmp -w /mnt/tmp modera/php5-fpm bash -c "./vendor/bin/phpunit"
配置TestHarness和演员
连接
为了创建浏览器,演员将尝试解析以下环境变量
- SELENIUM_HOST
- SELENIUM_CONNECTION_TIMEOUT
- SELENIUM_REQUEST_TIMEOUT
- SELENIUM_BROWSER - 要查看可用的浏览器预设列表,请参阅Selenium集成库的DesiredCapabilities类(例如'chrome'、'firefox')
因此,为了更改,例如,当使用Docker运行测试套件时更改主机,您需要使用-e
标志,例如
docker run --network=host -it --rm -v $(pwd):/mnt/tmp -w /mnt/tmp -e SELENIUM_HOST=http://my-host modera/php5-fpm bash -c "./vendor/bin/phpunit"
演员行为
行为用于告诉演员,你知道,他们在某些情况下应该如何表现。例如,您有一个“admin”演员,并希望配置其浏览器不会自动最大化,您可以这样做
$harness->getActor('admin')->disableBehaviour(Actor::BHR_AUTO_MAXIMIZE);
要查看可用的完整行为列表,请参阅Actor::BHR_*常量。
无头环境
为了能够记录正在运行的安全测试的视频,我们依赖以下组件:
-
Selenium - 启动并控制浏览器
-
XVFB - 用于启动Selenium节点,为Selenium打开的浏览器窗口创建虚拟帧缓冲区
-
FFMPEG - 连接到远程XVFB,并捕获其创建的虚拟屏幕
-
FFMPEG REST服务器 - 在安装了FFMPEG的主机上运行,服务器启动/停止视频录制并创建正确命名的视频文件
-
PHPUnit/Behat监听器 - 监听器向FFMPEG REST服务器发送请求,告知服务器何时开始录制视频,并最终停止并将视频输出到文件
All these components must run in one private sub-network or be publicly accessible from web, for this setup to work correctly all components must be able to connecto to each other.
在详细说明之前,值得一提的是,尽管下面描述的设置中大多数组件运行在不同的主机上(这为将来并行运行测试提供了更多灵活性),但在某些情况下,在相同的主机上运行多个组件也可能没问题——例如,在运行测试的主机上同时运行FFMPEG REST服务器/FFMPEG。
在无头环境中运行测试的最简单方法是使用随库提供的种子配置,该配置位于Resources/dockerized-video-recorder
目录中。在本手册中,我们假设您的应用程序正在使用Whaler配置Docker容器,将Resources/dockerized-video-recorder/.docker
和Resources/dockerized-video-recorder/ffmpeg-server-web
目录复制到您的根whaler.yml文件旁边,然后运行以下两个命令
- whaler create --config vendor/modera/selenium-tools/Resources/dockerized-video-recorder/whaler.yml
- whaler start
在我们可以继续到描述如何编写测试的部分之前,可能需要执行最后一步——检查ffmpeg-server-web/index.php
,确保其require
语句中autoload.php
文件的路径正确。
使用plain PHPUnit进行测试
如果您想使用PHPUnit运行测试,您需要稍微修改一下phpunit.xml.dist文件,我们需要添加一个监听器并配置RRL_ENDPOINT
环境变量,该文件将类似于以下内容
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="vendor/autoload.php"
>
<php>
<server name="RRL_ENDPOINT" value="vr-nginx" />
</php>
<listeners>
<listener class="Modera\Component\SeleniumTools\VideoRecording\RemotePHPUnitListener\RemoteReportingListener" />
</listeners>
<testsuites>
<testsuite name="Sample testcase">
<directory>./tests/</directory>
</testsuite>
</testsuites>
</phpunit>
RRL_ENDPOINT
由RemoteReportingListener用于确定“FFMPEG REST服务器”的位置。
由于之前我们是在主机机器上运行我们的AppearInTest测试,该测试使用在主机上运行的安全节点,但现在是在编排的dockerized环境中运行,我们需要更新负责建立服务器连接的代码片段
$driver = RemoteWebDriver::create(
'http://selenium-hub:4444/wd/hub', $capabilities, 15 * 1000, 30 * 1000
);
完成后,您可以使用whaler run php. "./vendor/bin/phpunit"
运行测试。测试运行完成后,请检查ffmpeg-server-web
目录以获取视频录制。
使用Behat进行测试
如果您想使用Behat运行测试,您需要在项目中安装Behat包(composer require behat/behat:^3.0
),并在项目目录的根目录下创建一个名为behat.yml
的文件。以下是一个Modera Foundation的配置示例文件
default:
extensions:
Modera\Component\SeleniumTools\Behat\ActorsExtension:
drivers:
default:
browser: chrome
host: "%BEHAT_SELENIUM_HOST%"
video_recorder:
host: "%BEHAT_VR_HOST%"
harnesses:
default:
driver: default
actors:
admin:
base_url: "%BEHAT_BACKEND_BASE_URL%"
john.doe:
base_url: "%BEHAT_BACKEND_BASE_URL%"
suites:
default:
contexts:
- Modera\Component\SeleniumTools\Behat\Context\MJRContext
- Modera\Component\SeleniumTools\Behat\Context\ExtJsGridContext
这里需要注意的主要是,我们告诉Behat使用“ActorsExtension”,该扩展负责配置演员以及视频录制器。在behat.yml配置文件中,您可以使用百分号将单词括起来以引用环境变量。正如您在特定的配置文件中看到的,我们告诉Behat尝试从环境中提取三个值
BEHAT_SELENIUM_HOST
- 运行Selenium的主机名。如果您使用Resources/dockerized-video-recorder/whaler.yml
种子配置文件,则必须使用http://selenium-hub:4444/wd/hub
为此变量。BEHAT_VR_HOST
- 运行FFMPEG REST服务器的主机名。如果使用种子whaler.yml
,则可以使用http://vr-nginx
。BEHAT_BACKEND_BASE_URL
- 在此特定场景中,此变量用于告诉演员应从哪个URL开始。
在您的 behat.yml
文件中,您可能不使用环境变量来存储配置,但避免在配置文件中存储依赖于环境的配置数据是一种良好的实践。
当使用上述配置文件(并且您已安装 Modera Foundation)时,为了运行测试(默认情况下位于 features 目录),您可以运行以下命令
whaler run php. "./bin/behat" -e BEHAT_SELENIUM_HOST=http://selenium-hub:4444/wd/hub -e BEHAT_VR_HOST=http://vr-nginx -e BEHAT_BACKEND_BASE_URL=http://nginx/app_dev.php/backend/
编写支持多用户 Behat 上下文文件的代码
创建一个支持多用户端到端测试的 Behat 上下文文件的最简单方法是扩展 \Modera\Component\SeleniumTools\Behat\Context\HarnessAwareContext
。要了解如何编写自己的 FeatureContext 文件,请查看以下两个链接
- Behat 官方指南:编写自定义 FeatureContext 文件
- 有关支持多用户的 FeatureContext 文件示例,请参阅
Modera\Component\SeleniumTools\Behat\Context\MJRContext
和Modera\Component\SeleniumTools\Behat\Context\ExtJsGridContext
。
测试 ExtJs/MJR 应用程序(实验性)
有关示例测试,请参阅 Modera Foundation。