Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Selenium & PHPUnit made easy with Steward (Berlin, April 2017)

Annotated slides from Berlin PHP Usergroup Meetup, 4th April 2017.
Not only unit tests but also end-to-end tests in real browser are important part of test automation and test pyramid. So let's have a look how to easily write and run Selenium functional tests using PHPUnit and Steward.

  • Be the first to comment

  • Be the first to like this

Selenium & PHPUnit made easy with Steward (Berlin, April 2017)

  1. 1. Selenium & PHPUnit made easy with Steward Berlin PHP Usergroup 4th April 2017 Ondřej Machulda @OndraM Annotated slides
  2. 2. Source:, author Fiestoforo, licence CC BY 3.0 Web app is a machine – like a bicycle. Lots of different parts in different layers, which needs to fit together to accomplish the one ultimate purpose of the machine – so you can ride the bike.
  3. 3. Source: UNIT TESTS FOR EVERYTHING! But we usually do test the machine disassembled on smallest piceses. Why? Becase it is easy to test them - you can easily define their behavior and you can usually easily and fast validate it! Like inputs/outputs of method and its beavior in edge cases. However, this it not always enough...
  4. 4. Functional system testing („end-to-end tests“ / „UI tests“) Source:, author Gianluca Gimini If you want to make sure the assembled machine works and everything fits together (eg. you can really drive & steew the bike), you will test the main business critical scenarios from customer point of view. Thats why these kind of tests is unreplaceable – its your output quality control. Next one in the QA chain is usually only the customer, and thats too late :-).
  5. 5. Test pyramid 5 % 15 % 80 % Test pyramid is a visual guide showing how much time you should be investing in which layer of tests. Why approx. this ratio? The higher layer, the harder is to write and maintain the tests and the slower feedback you have from them. Unit-tests are fast to write and run, stable and helps with code design – so as a developer you want to primary write them. But remember the bicycle – you could miss a lot without functional tests.
  6. 6. (WebDriver) The tool you need is Selenium - open-source library for browser automation. You tell it what actions in browser should be done and it executes them. The WebDriver protcol is also W3C draft standard and it is implemented in almost all browsers.
  7. 7. Start Selenium server $ docker run -p 4444:4444 selenium/standalone-firefox-debug OR $ java -jar selenium-server-standalone-3.3.1.jar &  localhost:4444 Selenium requires practically zero-config installation. You may start it using Docker or local jar file. Using any of the examples above will start Selenium server listening on port 4444.
  8. 8. Selenium server is platform and language independent (it just listens on the port), and there is a lot of libraries for many languages – inlcuding PHP. There are also multiple ways how to run tests in PHP, however we wanted to use PHPUnit, because we are already using it for our unit tests. And besides a different domain languguage the other tools (like Codeception, Behat...) has, we were also missing some key features we needed.
  9. 9. Install Steward $ mkdir selenium-tests $ composer require lmc-eu/steward PHPUnit + facebook/php-webdriver =  So here comes Steward, an open-source tool built on top of PHPUnit and Symfony components. It is a test-runner (controlled from CLI) and also extension for PHPUnit, integrating the php-webdriver library. The installation is quite easy, actually, nothing else is needed to start writing tests.
  10. 10. <?php namespace My; use LmcStewardTestAbstractTestCase; class GithubSearchTest extends AbstractTestCase { public function testShouldSubmitSearchFromHeaderAndShowResults() { $this->wd->get(''); $searchInput = $this->findByCss('.header-search-input'); $searchInput->sendKeys('symfony') ->submit(); $this->waitForTitle('Search · symfony · GitHub'); $firstItem = $this->findByCss('ul h3 a'); $this->assertSame('symfony/symfony', $firstItem->getText()); $firstItem->click(); $this->waitForTitle('GitHub - symfony/symfony: The Symfony PHP framework'); $headerText = $this->findByCss('h1')->getText(); $this->assertSame('symfony/symfony', $headerText); } } Here you can see some examples of what Selenium is capable of: load URL, locate elements, write to inputs, read meta titles, read text from elements, click on element etc. And even more complex actions like chaning the browser window size, executing javascript, navigating back in the history and so on.
  11. 11. Live Demo Repository with examples: Clone the GitHub repository and run the tests like this: $ cd selenium-tests $ ./vendor/bin/steward run prod firefox -vv See the repository for more description and more examples how to start the test execution.
  12. 12. Parallelization While unit-tests are executed in a matter of seconds, functional tests can take minutes or even more. To keep their execution time somehow reasonable, you have to parallelize and run multiple tests at once – what is one of the features Steward provides.
  13. 13. Error reporting When some test fails, Steward gathers PNG screenshot from the browser and also saves HTML snapshot of the DOM state of the webpage, so you can debug it later. It also provides results overview of the test execution progress – using generated webpage or CLI command.
  14. 14. Example of results.xml file as seen in browser. The test status is generated and updated during the whole run, so you can also watch the progress here.
  15. 15. Example output of `steward results` command – this is CLI equivalent of the reports.xml file.
  16. 16. Page Object Pattern Page Object is a design pattern from Martin Fowler, which suggest interacting with the webpage UI through an abstraction – ie. an object with methods mapping the UI structure and UI interactions. Because in the tests scenario you want to interact with the UI, not with its HTML implementation. Page objects are also a way how to make your functional tests maintainable in a long-term.
  17. 17. <?php namespace My; use LmcStewardTestAbstractTestCase; class GithubSearchTest extends AbstractTestCase { public function testShouldSubmitSearchFromHeaderAndShowResults() { $this->wd->get(''); $searchInput = $this->findByCss('.header-search-input'); $searchInput->sendKeys('symfony') ->submit(); $this->waitForTitle('Search · symfony · GitHub'); $firstItem = $this->findByCss('ul h3 a'); $this->assertSame('symfony/symfony', $firstItem->getText()); $firstItem->click(); $this->waitForTitle('GitHub - symfony/symfony: The Symfony PHP framework'); $headerText = $this->findByCss('h1')->getText(); $this->assertSame('symfony/symfony', $headerText); } } This is the original test as shown before, without using page objects. Source code of the file on GitHub
  18. 18. <?php namespace My; use LmcStewardTestAbstractTestCase; class GithubSearchUsingPageObjectTest extends AbstractTestCase { public function testShouldSubmitSearchFormAndShowSearchResults() { $this->wd->get(''); $navigationPanel = new NavigationPanel($this); $searchResultsPanel = $navigationPanel->submitSearchWithQuery('symfony'); $foundItems = $searchResultsPanel->getFoundItems(); $this->assertSame('symfony/symfony', $foundItems[0]); $projectDetail = $searchResultsPanel->openResultOnIndex(0); $projectDetailHeader = $projectDetail->getHeader(); $this->assertSame('symfony/symfony', $projectDetailHeader); } } This is the same test case scenario, but rewritten to use page objects. Source code of the file on GitHub
  19. 19. <?php namespace MyPanelGithub; use LmcStewardComponentAbstractComponent; class NavigationPanel extends AbstractComponent { const SEARCH_INPUT_SELECTOR = '.header-search-input'; /** * @param string $query * @return SearchResultsPanel */ public function submitSearchWithQuery($query) { $this->findByCss(self::SEARCH_INPUT_SELECTOR) ->sendKeys($query) ->submit(); $this->waitForTitle('Search · ' . $query . ' · GitHub'); return new SearchResultsPanel($this->tc); } } An example of NavigationPanel page object. Source code of the file on GitHub
  20. 20. Continuous integration & Continuous deployment Functional tests are also a necessary part of continuous integration and should not be missing in your continuous deployment pipeline. As you know – the faster you find your bugs, the faster and cheaper is to fix them!
  21. 21. Summary Not everything could be covered by unit tests Test pyramid should not be missing its top Functional tests may help you sleep better It is easy to start! Selenium & Steward Continuous integration & deployment
  22. 22.   examples source code  Ondřej Machulda   @OndraM