Home >Backend Development >PHP Tutorial >Using Selenium with PHPUnit
Testing is a really wide subject, whether it be unit testing, functional testing, acceptance testing, etc. In this article, we’re going to see how you can do acceptance testing using Selenium. I will use a practical example to illustrate a real use case. I will assume that you already know how to do unit testing using PHPUnit, or that you at least have a grasp of what it’s all about. Let’s get started.
Acceptance testing is the process of telling user stories through tests, and I love this quote to describe it:
A formal test conducted to determine whether or not a system satisfies its acceptance criteria and to enable the customer to determine whether or not to accept the system.
Selenium is a tool to automate user interface testing. It helps with testing your application against the browser. The process could be described like so:
You may be wondering: “How does it manipulate the web page using the described tests?”
The answer is “it depends”. If you’re using Selenium RC (previously named Selenium 1), it will inject auto generated JavaScript code to the page to perform the desired actions. Selenium RC is deprecated and is only supported in maintenance mode; you should be using Selenium WebDriver.
When using Selenium WebDriver (Selenium 2), the tests are translated into commands and passed to the Selenium server (more about that in a moment), then passed to the browser using the web browser native API.
Because we don’t actually have an application to test, I’m going to use a user registration page. The user will enter his personal information and some billing info. If everything is good, the page should output Everything is Good!. Otherwise, the page will show the subscription form with a list of validation error messages.
We will start testing our application using PHPUnit with the Selenium extension. Be sure to install them using Composer before starting.
<span>composer require --dev phpunit/phpunit </span><span>composer require --dev phpunit/phpunit-selenium</span>
We said before that commands are passed to a Selenium server, which then forwards them to the browser. We need to download the Selenium server, which is just a JAVA archive executable. The server can be run using java -jar selenium-server-standalone-
<span>alias sserve="java -jar /usr/local/bin/selenium-server-standalone-<version>.jar"</span>
PHPUnit supports both Selenium RC and WebDriver, and it provides two classes for that purpose. The PHPUnit_Extensions_SeleniumTestCase is used for the RC version, and the PHPUnit_Extensions_Selenium2TestCase is used for the WebDriver version. So, your test must extend one of them to get started. Please remember that the RC version is being deprecated, so we’ll use the WebDriver one in our example below.
<span>// tests/acceptance/UserSubscriptionTest.php </span> <span>class UserSubscriptionTest extends PHPUnit_Extensions_Selenium2TestCase </span><span>{ </span> <span>public function setUp() </span> <span>{ </span> <span>$this->setHost('localhost'); </span> <span>$this->setPort(4444); </span> <span>$this->setBrowserUrl('http://vaprobash.dev'); </span> <span>$this->setBrowser('firefox'); </span> <span>} </span><span>}</span>
The setUp method is used for preparing the test environment. In this case, we use it to tell PHPUnit where our Selenium server is running, what browser we’ll be using and the URL of our application. The setHost method defaults to localhost and the setPort method defaults to 4444, so they can be omitted here. However, this can be used if your testing server is inside a Windows machine that supports Internet Explorer while you run your tests from another different machine, etc.
The tearDown method is called when the tests are done, and it’s used to clear the stage. We use it to close the browser and terminate the current session.
<span>public function tearDown() </span><span>{ </span> <span>$this->stop(); </span><span>}</span>
PHPUnit data providers allow us to feed our tests with specific data without having to iterate over it. You can read more in the documentation.
<span>// tests/acceptance/UserSubscriptionTest.php </span> <span>class UserSubscriptionTest extends PHPUnit_Extensions_Selenium2TestCase </span><span>{ </span> <span>public function validInputsProvider() </span> <span>{ </span> <span>$inputs[] = [ </span> <span>[ </span> <span>'username' => 'younesrafie', </span> <span>'password' => 'mypassword', </span> <span>'password_confirmation' => 'mypassword', </span> <span>'email' => 'mymail@gmail.com', </span> <span>'cardHolderName' => 'RAFIE Younes', </span> <span>'cardNumber' => '378282246310005', </span> <span>'billingAddress' => 'Narjiss B Fez Morocco', </span> <span>'cvc' => '850', </span> <span>'expirationMonth' => '01', </span> <span>'expirationYear' => '2016', </span> <span>] </span> <span>]; </span> <span>return $inputs; </span> <span>} </span> <span>public static function invalidInputsProvider() </span> <span>{ </span> <span>$inputs[] = [ </span> <span>[ </span> <span>'username' => '@younesrafie', </span> <span>'password' => 'mypassword', </span> <span>'password_confirmation' => 'mypassword', </span> <span>'email' => 'mymail@gmail.com', </span> <span>'cardHolderName' => 'RAFIE Younes', </span> <span>'cardNumber' => '378282246310005', </span> <span>'billingAddress' => 'Narjiss B Fez Morocco', </span> <span>'cvc' => '850', </span> <span>'expirationMonth' => '01', </span> <span>'expirationYear' => '2016', </span> <span>], </span> <span>"Username must only contain alpha numeric characters and dashes." </span> <span>]; </span> <span>// ... </span> <span>return $inputs; </span> <span>} </span><span>}</span>
The invalidInputsProvider returns a list of valid inputs except for one field, and we pass along the expected error message after the validation fails.
One common task when working with web pages is element selection. PHPunit’s Selenium extension provides a really nice API for that. You can select elements by class name, tag, name, ID, CSS selector, XPath, etc. The method will return a PHPUnit_Extensions_Selenium2TestCase_Element instance which you can use to select other child elements, attributes, etc. You can also set or get the element value, update element CSS and a bunch of other common tasks. For our page we may do something like the following.
<span>composer require --dev phpunit/phpunit </span><span>composer require --dev phpunit/phpunit-selenium</span>
This test will select the username input and set a value, then submit the subscription form. We can add an assertion after that to see if the response is as expected. The page body will contain Everything is Good! if the validation passed.
<span>alias sserve="java -jar /usr/local/bin/selenium-server-standalone-<version>.jar"</span>
Our data provider contains the input name and the corresponding value. We will create a separate method to handle filling the form inputs and submitting.
<span>// tests/acceptance/UserSubscriptionTest.php </span> <span>class UserSubscriptionTest extends PHPUnit_Extensions_Selenium2TestCase </span><span>{ </span> <span>public function setUp() </span> <span>{ </span> <span>$this->setHost('localhost'); </span> <span>$this->setPort(4444); </span> <span>$this->setBrowserUrl('http://vaprobash.dev'); </span> <span>$this->setBrowser('firefox'); </span> <span>} </span><span>}</span>
To point the browser to a specific page we use the url method from the PHPUnit_Extensions_Selenium2TestCase class. The URL is relative to the one provided to the setBrowserUrl method. So, after pointing the browser to the index page we fill and submit the form, then test the expected success message.
<span>public function tearDown() </span><span>{ </span> <span>$this->stop(); </span><span>}</span>
Assuming your Selenium server is up and running, go ahead and run your tests with phpunit tests/acceptance/UserSubscriptionTest.php. This will create a new browser session and start filling the form. We are expecting everything to pass with one successful assertion.
Some of the tests fail, and the testing duration is too short for us to observe what went wrong. PHPUnit has the ability to capture screenshots of failing tests using the currentScreenshot method which returns a BLOB image that we can save.
<span>// tests/acceptance/UserSubscriptionTest.php </span> <span>class UserSubscriptionTest extends PHPUnit_Extensions_Selenium2TestCase </span><span>{ </span> <span>public function validInputsProvider() </span> <span>{ </span> <span>$inputs[] = [ </span> <span>[ </span> <span>'username' => 'younesrafie', </span> <span>'password' => 'mypassword', </span> <span>'password_confirmation' => 'mypassword', </span> <span>'email' => 'mymail@gmail.com', </span> <span>'cardHolderName' => 'RAFIE Younes', </span> <span>'cardNumber' => '378282246310005', </span> <span>'billingAddress' => 'Narjiss B Fez Morocco', </span> <span>'cvc' => '850', </span> <span>'expirationMonth' => '01', </span> <span>'expirationYear' => '2016', </span> <span>] </span> <span>]; </span> <span>return $inputs; </span> <span>} </span> <span>public static function invalidInputsProvider() </span> <span>{ </span> <span>$inputs[] = [ </span> <span>[ </span> <span>'username' => '@younesrafie', </span> <span>'password' => 'mypassword', </span> <span>'password_confirmation' => 'mypassword', </span> <span>'email' => 'mymail@gmail.com', </span> <span>'cardHolderName' => 'RAFIE Younes', </span> <span>'cardNumber' => '378282246310005', </span> <span>'billingAddress' => 'Narjiss B Fez Morocco', </span> <span>'cvc' => '850', </span> <span>'expirationMonth' => '01', </span> <span>'expirationYear' => '2016', </span> <span>], </span> <span>"Username must only contain alpha numeric characters and dashes." </span> <span>]; </span> <span>// ... </span> <span>return $inputs; </span> <span>} </span><span>}</span>
The invalid form submission is almost identical to the previous method. We fill in the form inputs and submit. Then, we verify that the validation error message is as expected. We will be using the invalidInputsProvider I mentioned earlier.
<span>class UserSubscriptionTest extends PHPUnit_Extensions_Selenium2TestCase </span><span>{ </span> <span>public function testFormSubmissionWithUsername() </span> <span>{ </span> <span>$this->byName('username')->value('younesrafie'); </span> <span>$this->byId('subscriptionForm')->submit(); </span> <span>} </span><span>}</span>
The byCssSelector method allows us to retrieve an element from the page using CSS selectors, in this case the error paragraph. We assert if the error message is as expected using the error message field from the data provider method.
Our form contains only basic interactions like selecting elements, setting values, submitting the form, etc. However, we can also use the click method on a button or link element to verify that the target page is working as expected.
We used the Firefox browser for our tests. However, we have the ability to use any other browser as well. Selenium uses the driver approach, where every browser vendor works on providing its own driver. You can check the list of supported drivers in the documentation.
To enable the Chrome browser, you need to download the chromeDriver and specify the path as an option when launching the Selenium server.
<span>composer require --dev phpunit/phpunit </span><span>composer require --dev phpunit/phpunit-selenium</span>
<span>alias sserve="java -jar /usr/local/bin/selenium-server-standalone-<version>.jar"</span>
If your page content is loaded via AJAX, and you don’t want to trigger the tests directly on page load, you’ll want to wait until your page is loaded and your elements are present.
<span>// tests/acceptance/UserSubscriptionTest.php </span> <span>class UserSubscriptionTest extends PHPUnit_Extensions_Selenium2TestCase </span><span>{ </span> <span>public function setUp() </span> <span>{ </span> <span>$this->setHost('localhost'); </span> <span>$this->setPort(4444); </span> <span>$this->setBrowserUrl('http://vaprobash.dev'); </span> <span>$this->setBrowser('firefox'); </span> <span>} </span><span>}</span>
The callback function will wait until we return a non null value, and will timeout after two seconds with an error message. The lookup method will keep looking for the element, but if you want to specify a searching interval you can use the implicitWait method.
<span>public function tearDown() </span><span>{ </span> <span>$this->stop(); </span><span>}</span>
This article was a brief introduction to using Selenium with PHPUnit for acceptance testing. In general, you can use Selenium for anything that requires browser automation. If you have any comments or questions, be sure to post them below and I will do my best to answer them.
To install PHPUnit and Selenium WebDriver in PHP, you need to use Composer, a dependency management tool for PHP. First, install Composer if you haven’t already. Then, run the following command in your terminal to install PHPUnit: composer require --dev phpunit/phpunit ^9. For Selenium WebDriver, use the command: composer require --dev php-webdriver/webdriver. This will install both PHPUnit and Selenium WebDriver in your PHP project.
To run a PHPUnit test with Selenium, you need to write a test case that uses Selenium WebDriver. In your test case, you can use WebDriver commands to interact with the browser. Once your test case is ready, you can run it using the PHPUnit command-line tool. Simply navigate to your project directory in the terminal and run the command: phpunit MyTest.php, where ‘MyTest.php’ is the name of your test file.
Assertions are used in PHPUnit to verify that a certain condition is true. They are essential for determining whether a test has passed or failed. In a PHPUnit test with Selenium, you can use assertions to check the state of web elements. For example, you can assert that a certain element is present, visible, or contains a specific text. To do this, you can use the assert methods provided by PHPUnit, such as assertEquals, assertTrue, or assertContains.
Handling browser sessions in PHPUnit with Selenium is done using the WebDriver’s session methods. When you create a new instance of the WebDriver, a new browser session is started. You can interact with this session using various methods, such as navigate(), refresh(), or close(). To end the session, you can use the quit() method, which will close all windows and end the session.
Handling AJAX requests in PHPUnit with Selenium can be tricky because you need to wait for the AJAX call to complete before you can interact with the updated elements. Selenium WebDriver provides the WebDriverWait class for this purpose. You can use it to wait for a certain condition to be true before proceeding. For example, you can wait for an element to be visible or clickable.
Selenium WebDriver supports multiple browsers, including Chrome, Firefox, Safari, and Internet Explorer. To run your PHPUnit tests on a different browser, you need to create a new instance of the WebDriver for that browser. For example, to use Firefox, you would create a new FirefoxDriver instance. Then, you can use this driver to run your tests.
Frames and iframes can be handled in PHPUnit with Selenium using the switchTo() method of the WebDriver. This method allows you to switch the context to a different frame or iframe. Once you’re done interacting with the elements inside the frame, you can switch back to the main content using the switchTo()->defaultContent() method.
Taking screenshots with Selenium in PHPUnit tests can be done using the takeScreenshot() method of the WebDriver. This method takes a screenshot of the current window and returns it as a string in PNG format. You can then save this string to a file to create the screenshot.
Cookies can be handled in PHPUnit with Selenium using the manage()->getCookies() method of the WebDriver. This method returns all cookies as an array. You can also use the manage()->addCookie(), manage()->deleteCookie(), and manage()->deleteAllCookies() methods to manipulate cookies.
Running PHPUnit tests with Selenium in parallel can significantly speed up your test suite. This can be achieved using a Selenium Grid, which allows you to run tests on multiple machines and browsers simultaneously. To use Selenium Grid, you need to set up a hub and nodes, and then configure your WebDriver to connect to the hub.
The above is the detailed content of Using Selenium with PHPUnit. For more information, please follow other related articles on the PHP Chinese website!