This extensions allows to run functionaln tests using WebDriver functions from Selenium Server 2.0. WebDriver runs as a plugin in remote browser, so it is much more reliable than standard Selenium test injected through JavaScript.
Extension is using PHP WebDriver Bindings project from http://code.google.com/p/php-webdriver-bindings/
Requirements ¶
Requires PHP WebDriver Bindings (they are packaged in this extension) and Selenium Server 2.0 as tests runtime environment. Should work with Firefox and Chrome browsers (they are supported by Selenium WebDriver)
Usage ¶
Extract archive in your extensions directory. Write functional test that extend CWebDriverTestCase in your tests/functional directory as usual.
Example:
Yii::import( 'ext.webdriver-bindings.CWebDriverTestCase' );
class ExampleTest extends CWebDriverTestCase {
protected function setUp() {
parent::setUp( 'localhost', 4444, 'firefox' );
}
public function testGoogle() {
$this->get( 'http://www.google.com/' );
$qElem = $this->findElementBy( LocatorStrategy::name, 'q' );
$this->assertNotNull( $qElem, 'There is no "query" element!' );
$qElem->sendKeys( array( 'yii framework' ) );
$qElem->submit();
sleep( 1 );
$elem = $this->findElementBy( LocatorStrategy::className, 'vsc' );
$this->assertNotNull( $elem, 'Results not found!' );
$this->assertTrue( $this->isTextPresent( 'Yii Framework' ), 'The is no "Yii Framework" text on result page!' );
}
}
or other way (based on CDbTestCase instead of WebTestCase):
define( 'TEST_BASE_URL', 'http://www.google.com/' );
Yii::import( 'ext.webdriver-bindings.CWebDriverDbTestCase' );
class ExampleDbTest extends CWebDriverDbTestCase {
public $baseUrl = TEST_BASE_URL;
public function testGoogle() {
$this->get( 'http://www.google.com/' );
$qElem = $this->findElementBy( LocatorStrategy::name, 'q' );
$this->assertNotNull( $qElem, 'There is no "query" element!' );
$qElem->sendKeys( array( 'yii framework' ) );
$qElem->submit();
sleep( 1 );
$elem = $this->findElementBy( LocatorStrategy::className, 'vsc' );
$this->assertNotNull( $elem, 'Results not found!' );
$this->assertTrue( $this->isTextPresent( 'Yii Framework' ), 'The is no "Yii Framework" text on result page!' );
}
}
difference with WebTestCase
Hi
could you please tell us a bit more about the differences between WebDriver and the current WebTestCase ? what are the added values ? is the API richer ?
Difference between classic Selenium and Webdriver (Selenium 2)
Selenium 1 used Javascript to control browser. It was fine for simple web apps, but the more ajax/other javascript logic was embedded in application the more problems it created.
Webdriver (now integral part of Selenium2) uses native browser extension (each browser needs its own implementation) which is controlled by test script. Webdriver tests script can connect directly to controll browser (for example using FirefoxDriver) or remotely through Selenium Server (using JsonWireProtocol).
Generally WebDriver approach is much faster and reliable.
I am not sure if there was possibility to make screenshots in standard Selenium, but WebDriver provides function to do this (i.e. when test fail). You can find more information at http://google-opensource.blogspot.com/2009/05/introducing-webdriver.html and http://code.google.com/p/php-webdriver-bindings/wiki/DifferenceBetweenWebdriverAndSelenium1?ts=1310633014&updated=DifferenceBetweenWebdriverAndSelenium1
Implemented WebDriver API methods
Great work!
It would be nice to see which methods are already implemented. I see there's a page place holder here http://code.google.com/p/php-webdriver-bindings/wiki/implemented_methods but it says "todo" as of the moment...
API and PhpWebDriver bindings
We are waiting for both WebDriver API for PHP providers to merge their code and then we will provide support for this final bindings. I will inform you about new version when available.
API and PhpWebDriver bindings
I found the list of implemented API methods here:
extensions/webdriver-bindings/phpwebdriver/status.html
Sharing the browser session between all tests in a test case
I have been experimenting with this extension and I found that it opens a new browser session for each test method of a test case. This is because new session is created in setUp() and destroyed in tearDown().
I find this not convenient because of two reasons:
First, it takes time to close and open a new browser window. The time overhead is not too big though, compared to the time it takes to load the db fixtures.
Second, it seems that cookies are not preserved across the browser sessions. This might be a good thing because you always start from the same environment. But it also means that you have to simulate user login in the beginning of every test method, or, alternatively, put all assertions in a big single test method rather than breaking it into separate tests.
If you want to share the browser session between all tests, here's solution that worked for me:
1. I changed the definition of CWebDriverTestCase:
~
`
phpclass CWebDriverTestCase extends CDbTestCase {
public $baseUrl; /** * @var WebDriver this is the local copy of shared webdriver */ protected $webdriver; /** * @var WebDriver this is the shared webdriver instance */ private static $_webdriver; /** * @var string hostname where selenium server is run */ protected static $host = 'localhost'; /** * @var integer port on which selenium server listens */ protected static $port = 4444; /** * @var string browser that we want to test against */ protected static $browser = 'firefox'; const LOAD_DELAY = 500000; //0.5 sec. delay const STEP_WAITING_TIME = 0.5; //when synchronous request is simulated this is single step waiting time const MAX_WAITING_TIME = 4; //when synchronous request is simulated this is total timeout when witing for result public static function setUpBeforeClass() { self::$_webdriver = new WebDriver(self::$host, self::$port); self::$_webdriver->connect(self::$browser); } public static function tearDownAfterClass() { if (self::$_webdriver) { self::$_webdriver->close(); } } public function setUp() { $this->webdriver = self::$_webdriver; parent::setUp(); }
...
`
~IMPORTANT: Please note that it is extending from
CDbTestCase
rather than fromCWebTestCase
. I could do this because I was writing tests from scratch and was not going to use any of the old Selenium 1 API calls, so I didn't need to extend from PHPUnit_Extension_SeleniumTestCase. Please note that extending fromCWebTestCase
won't work, because PHPUnit will not call setUpBeforeClass() and tearDownAfterClass() in this case.2. Then I modified WebTestCase class in my project like this:
<?php define('TEST_BASE_URL','http://fr.test/'); Yii::import( 'ext.webdriver-bindings.CWebDriverTestCase' ); class WebTestCase extends CWebDriverTestCase { }
I modified it to extend from CWebDriverTestCase. In your project you can override the host, port and browser in this class if needed. I didn't need to override, so this class is essentially empty.
3. Now I can create test cases like this:
<?php class SiteTest extends WebTestCase { public $fixtures = array( 'user' => 'User', ); public function testLogin() { // Assume we are not logged in $this->get(TEST_BASE_URL); $this->assertTrue($this->isTextPresent('Please login')); /* @var $element WebElement */ $element = $this->findElementBy(LocatorStrategy::id, "LoginForm_email"); $element->clear(); $element->sendKeys("kuzya@fr.test"); $element = $this->findElementBy(LocatorStrategy::id, "LoginForm_password"); $element->clear(); $element->sendKeys("12345"); $element = $this->findElementBy(LocatorStrategy::id, "LoginForm_rememberMe"); $element->click(); $element = $this->findElementBy(LocatorStrategy::name, "yt0"); $element->click(); $this->assertTrue($this->isTextPresent('Welcome, Kuzya!')); } public function testFeature() { $this->markTestIncomplete(); } public function testLogout() { /* @var $element WebElement */ $element = $this->findElementBy(LocatorStrategy::linkText, "Logout"); $element->click(); $this->assertTrue($this->isTextPresent('Please login')); } }
4. A couple more minor changes:
I removed the
<selenium>.. </selenium>
section from thephpuni.xml
because it won't work properly with the new extension.Finally, I made a little change in the bootstrap.php, because WebTestCase.php needs to be included after the application is created, otherwise 'ext' path alias won't work:
<?php $yiit='/opt/yii/framework/yiit.php'; $config=dirname(__FILE__).'/../config/test.php'; defined('YII_DEBUG') or define('YII_DEBUG', true); require_once($yiit); Yii::createWebApplication($config); Yii::getLogger()->autoFlush = 1; Yii::getLogger()->autoDump = true; require_once(dirname(__FILE__).'/WebTestCase.php');
Hope, it helps!
New version
Just uploaded new version of our extension.
Changelog:
Problem with Fixtures
Hi,
i have encountered a problem with database fixtures and CWebDriverDbTestCase this morning.
CWebDriverDbTestCase is overwriting the magic __call method to call methods on its webdriver object.
But CDbTestCase is using call to redirect calls to fixtures as well. CWebDriverDbTestCase should redirect unknown methods to parent::call.
Change method __call in CWebDriverDbTestCase (~line 95) to
public function __call( $name, $arguments ) { if( method_exists( $this->webdriver, $name ) ) { return call_user_func_array( array( $this->webdriver, $name ), $arguments ); } else { return parent::__call($name, $arguments); } }
version 1.1b
Just posted version with proper __call handling.
ERROR - Exception running 'getLocation 'command on session null
Does anyone had this problem?
I just started the server by: java -jar selenium-server-standalone-2.25.0.jar
And runned the test by: phpunit functional/SiteTestWebDriver.php
10:57:41.201 ERROR - Exception running 'getLocation 'command on session null java.lang.NullPointerException: sessionId should not be null; has this session been started yet? at org.openqa.selenium.server.FrameGroupCommandQueueSet.getQueueSet(FrameGroupCommandQueueSet.java:220) at org.openqa.selenium.server.commands.SeleniumCoreCommand.execute(SeleniumCoreCommand.java:55) at org.openqa.selenium.server.SeleniumDriverResourceHandler.doCommand(SeleniumDriverResourceHandler.java:613) at org.openqa.selenium.server.SeleniumDriverResourceHandler.handleCommandRequest(SeleniumDriverResourceHandler.java:407) at org.openqa.selenium.server.SeleniumDriverResourceHandler.handle(SeleniumDriverResourceHandler.java:151) at org.openqa.jetty.http.HttpContext.handle(HttpContext.java:1526) at org.openqa.jetty.http.HttpContext.handle(HttpContext.java:1479) at org.openqa.jetty.http.HttpServer.service(HttpServer.java:914) at org.openqa.jetty.http.HttpConnection.service(HttpConnection.java:820) at org.openqa.jetty.http.HttpConnection.handleNext(HttpConnection.java:986) at org.openqa.jetty.http.HttpConnection.handle(HttpConnection.java:837) at org.openqa.jetty.http.SocketListener.handleConnection(SocketListener.java:243) at org.openqa.jetty.util.ThreadedServer.handle(ThreadedServer.java:357) at org.openqa.jetty.util.ThreadPool$PoolThread.run(ThreadPool.java:534) 10:57:41.202 INFO - Got result: ERROR Server Exception: sessionId should not be null; has this session been started yet? on session null
Am I missing something?
Running on ubuntu 12.04 / firefox 15.0.1.
Thank you very much.
CWebDriverTestCase setBrowserUrl() not working
I am using a selenium node and hub and trying to use the example that comes with the CWebDriverTestCase extension.
Even if I put this in setUp ...
$this->setBrowserUrl('localhost');
I get the following error ...
It appears to me that setBrowserUrl() sets the $baseUrl property, but that the error source seems to want the $browserUrl property to be set.
Does anyone have some help for me?
update 1.2
Updated webdriver bindings files according to its google code repo. changes are necessary for this extension to work with new Selenium server versions (session handling changed).
pvk and Bagger1 - please check if those changes fix your problems
IE click problem vs. native events
Hello again,
I've been struggling to get Internet Explorer 11 to click on a link. (Apparently, it has problems if the browser window doesn't have focus.)
I finally made
nativeEvents => true
in the$allCaps
array inWebDriver->connect()
, and it worked for me.I see that there is an optional parameter ($caps) on
WebDriver->connect()
, but I don't see how to get a custom array sent in to it, since theCWebDriverTestCase->startUp
function doesn't include it.Is there a good way to get the capabilities modified when starting a new test?
Re: IE click problem vs. native events
passing caps is probably something that should be on our TODO list... as a workaround you can still override startUp method but you need to to load fixtures by yourself (as in oryginal method)
Sock Proxy
Hi, How does it work with Proxy (Sock5)?
I using Tor to get content of html of urls.
How can I config sock port as following:
host:port
Test for run player
Hi community
I have a project podcast with resources audio/video and image, and description.
Now, I have a question, the item this extension can help me, for got object.
Greetings
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.