Symfony 3 end to end testing with mink and selenium
We all know how important end-to end testing is, but offten on “fast passed” projects, we don’t find the time to implement best practices. Here I will show you how to setup circleCI with selenium
I’ll assume you already have a Symfony 3 project up and running
cd path/to/project
composer require behat/mink
composer require behat/mink-selenium2-driver
mkdir tests/AppBundle/Mink
touch tests/AppBundle/Mink/CoreMink.php
- enter this content in the file:
<?php
namespace Tests\AppBundle\Mink;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Behat\Mink\Driver\Selenium2Driver;
use Behat\Mink\Exception\ElementNotFoundException;
use Behat\Mink\Session;
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
abstract class CoreMink extends WebTestCase
{
/** @var string */
private $minkBaseUrl;
/** @var Session */
protected $minkSession;
/** @var Client */
protected $client;
/** @var Router */
protected $router;
/** @var string */
protected $seleniumDriverType;
public function setUp()
{
$this->client = static::createClient();
$container = $this->client->getContainer();
$this->router = $container->get('router');
$this->minkBaseUrl = $container->getParameter('mink_url');
$this->seleniumDriverType = $container->getParameter('selenium_driver_type');
}
/**
* @before
*/
public function setupMinkSession()
{
$driver = new Selenium2Driver($this->seleniumDriverType);
$this->minkSession = new Session($driver);
$this->minkSession->start();
}
public function getCurrentPage()
{
return $this->minkSession->getPage();
}
public function getCurrentPageContent()
{
return $this->getCurrentPage()->getContent();
}
public function visit($url)
{
$this->minkSession->visit($this->minkBaseUrl.$url);
}
public function fillField($field, $value)
{
$page = $this->getCurrentPage();
try {
$page->fillField($field, $value);
} catch (ElementNotFoundException $ex) {
$this->screenShot();
throw($ex);
}
}
public function find($type, $value)
{
$page = $this->getCurrentPage();
try {
return $page->find($type, $value);
} catch (ElementNotFoundException $ex) {
$this->screenShot();
throw($ex);
}
}
public function findField($type)
{
$page = $this->getCurrentPage();
try {
return $page->findField($type);
} catch (ElementNotFoundException $ex) {
$this->screenShot();
throw($ex);
}
}
public function pressButton($field)
{
$page = $this->getCurrentPage();
try {
$page->pressButton($field);
} catch (ElementNotFoundException $ex) {
$this->screenShot();
throw($ex);
}
}
public function login($user, $pass)
{
$this->getCurrentPageContent();
$this->fillField('_email', $user);
$this->fillField('_password', $pass);
$this->pressButton('connexion');
}
public function clickLink($field)
{
$page = $this->getCurrentPage();
try {
$page->clickLink($field);
} catch (ElementNotFoundException $ex) {
$this->screenShot();
throw($ex);
}
}
public function screenShot()
{
$driver = $this->minkSession->getDriver();
if (!($driver instanceof Selenium2Driver)) {
$this->minkSession->getDriver()->getScreenshot();
return;
} else {
$screenShot = base64_decode($driver->getWebDriverSession()->screenshot());
}
$timeStamp = (new \DateTime())->getTimestamp();
file_put_contents('/tmp/'.$timeStamp.'.png', $screenShot);
file_put_contents('/tmp/'.$timeStamp.'.html', $this->getCurrentPageContent());
}
public function logout()
{
$page = $this->getCurrentPage();
$page->clickLink('logout');
}
}
- Install chromedriver somewhere in your path: https://sites.google.com/a/chromium.org/chromedriver/
- download selenium server standalone, I have been using version 2.53: http://selenium-release.storage.googleapis.com/index.html?path=2.53/
- create your first mink selenium test!
touch tests/AppBundle/Mink/FirstTest.php
<?php
namespace Tests\AppBundle\Mink;
class FistTest extends CoreMink
{
public function testSubmitPage()
{
$this->visit('/client/checkout');
$this->login('sdevilcry@gmail.com', 'toto'); // Login first.
}
}
- run selenium server standalone
java -jar /path-to/selenium-standalone.jar
- run phpunit
vendor/bin/phpunit tests/AppBundle/Mink
- if you have followed the steps correctly, chrome will open and run your initial test, Awsome!
Now lets add this to our circleci.yml config
general:
artifacts:
- "/tmp/"
- "var/logs/"
machine:
hosts:
sharegroop.dev: 127.0.0.1
php:
version: 7.0.4
timezone:
Europe/Paris
environment:
SYMFONY_ENV: test
dependencies:
pre:
- curl http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.1.jar > selenium-server-standalone.jar
- curl http://chromedriver.storage.googleapis.com/2.23/chromedriver_linux64.zip | gzip -dc > chromedriver
- chmod +x chromedriver
- 'java -jar selenium-server-standalone.jar -trustAllSSLCertificates -Dwebdriver.chrome.driver=chromedriver':
background: true
- google-chrome --version
- wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
- sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb stable main" >> /etc/apt/sources.list.d/google.list'
- sudo apt-get update
- sudo apt-get --only-upgrade install google-chrome-stable
- google-chrome --version
override:
- cp app/config/parameters.test.yml app/config/parameters.yml
- composer install --no-interaction
- 'bin/console server:run':
background: true
Ok, this should be enough but unfortunatly right now there is a bug in behat/mink-selenium2-driver repository, its fixed in this pull request https://github.com/minkphp/MinkSelenium2Driver/pull/244
so you may need to install from that branch instead of master in order to get chrome to work in headless mode.
Hope this helps!