Testing the Symfony2 app with MinkBundle, PhantomJS and PHPUnit
In this post Shashikant Jagtap, Senior Developer in test working for BBC/Programmes, shares his experiences setting up MinkBundle with PHPUnit for the web testing of the Symfony2 application, using PhantomJS based GhostDriver and Doctrine fixtures.
Please note: All code samples for this post are available on GitHub here.
We used a comibination of MinkBundle, PHPUnit, PhantomJS and Doctrine to automate our web tests - this gives a high level of confidence to the whole team when developing an application. As long as testing pyramid is considered, the application should have unit and integration tests which are considered as fast and solid. On top of that, it should also have functional tests which are considered as slow and brittle, but do add value and confidence within the team.
- The application involves JS & Ajax interactions.
- We have to test it against different browsers and switch between them.
- There are different user journeys to be considered while navigating to the pages to fill the forms.
In order to deal with problems with WebTestCase, we found MinkBundle. It's a Symfony2 bundle which can be used in any Symfony project. There are various benefits Mink can provide:
- We can choose JS or non JS drivers as Mink has different drivers like Selenium, Zombie, Goutte.
- We can traverse on the pages and perform some actions. Mink API's have lots of actions.
- Mink provides an inbuilt assertion library called WebAssert which allows us to make smarter assertions.
- MinkBundle allows us to access containers and use doctrine fixtures or other similar fixture mechanism.
Setting up MinkBundle in the existing Symfony2 project is pretty easy - basically its a 3 step process:
- MinkBundle and Selenium WebDriver dependencies in your 'composer.json' file.
- Register the bundle in the app/AppKernel.php directory.
- Update Kernel configuration in 'app/config/config_test.yml' to include Mink config.
We can always set a base URL where we are going to execute tests. Currently its setup for the local host. Now we are ready to use Mink bundle.
GhostDriver is a PhantomJS based wrapper around Selenium WebDriver. Mink has various drivers like Selenium, Goutte, Zombie and Sahi, but we can use PhantomJS based GhostDriver to drive tests without launching a browser. Assuming you have installed PhantomJS, then launch GhostDriver on any available port:
$ phantomjs --webdriver=8643
Now GhostDriver is listening to "http://localhost:8643/wd/hub" and we have configured that path in our Mink configuration.
PHPUnit Test Setup
- Mink Session setup.
- Web Assert setup.
- Fixture Setup using Lipps functional tests bundle.
Mink has the ability to perform user actions on the page like clicking, filling forms, accept/deny pop-up, scroll up and down etc. There are various Mink API's we can use to automate browser tasks. Mink has an awesome library called 'WebAssert' which allows us to make various assertions on the browser behaviours. Please have a look at Web Assert API's to see the complete details. You can view sample code for BaseMinkPHPUnitTestCase class here.
Mink WebAssert has great assertions which can be used while performing browser actions, but it has a couple of issues while using with PHPUnit.
- When test fails, it throws an exception rather than error.
- Mink WebAssert assertions isn’t counted by PHPUnit.
We need to make some amendments to make it play better with PHPUnit. We wrote "WebAssertProxy" in order to deal with PHPUnit. We have used Lipps functional test fixtures to get fixture data loaded in to tests before execution and tear down after test execution. Sample code for the WebAssettProxy is here.
Writing & Running Tests
At this stage, we are good to write our first functional tests. We can write our test exactly how we write PHPUnut but will extend to MinkBaseClass. Again, writing tests involves following steps:
- Load Fixtures.
- Open Page and perform Action using Mink.
- Assert elements on pages with WebAssert.
- Tear Down Fixture.
Simple homepage tests will look like this.
In order to run those tests we need to make sure that the local web server and GhostDriver services are started. We can launch local web server using symfony as:
$ php app/console run:server
And again we can launch GhostDriver as:
$ phantomjs --webdriver=8643
Assuming we have edited 'app/phpunit.dist.xml'. Now we are ready to run our tests using:
$ bin/phpunit -c app
Junit reports are logged if you use the default phpunit configuration, which can be then plugged into the Continuous Integration server. Additionally, you can created separate 'mink-phpunit.xml' to run only JS related browser tests.