I do love using Grunt Watch for all my development compiling and running automated tests. One down side when using PhantomJS with codeception however is that you need to manually spawn an instance (typically with phantomjs --webdriver=4444
) before you can run your codeception tests in another window. This doesn't suit me and thus I've discovered a nice little package for grunt called grunt-run.
Basically when I want to run my tests, I hit grunt test - which spawns an instance of phantomjs, runs codeception, and then terminates phantomjs once codeception is finished. Here's my grunt file.
I've made a little repo for you to play with as a starting point (or if you want to see each step involved through tags. Available at https://github.com/darrennolan/codecept-phantomjs
I assume the following
- NodeJS / NPM Installed.
- Composer installed globally, or where I call
composer
by itself, havecomposer.phar
downloaded already and usephp composer.phar
- Codeception installed globally, or where I call
codecept
by itself, usephp vendor/bin/codecept
- grunt-cli installed globally, or where I call
grunt
by itself, use./node_modules/.bin/grunt
First up, lets get started getting grunt on board by installing the node modules we'll be using, as well as phantomJS. By using the fantastic package for NPM PhantomJS package, the binary is automatically selected based on your current operating system (I develop on Windows and Mac, and typically test and run production on Linux) so this is perfect and ideal for all these scenarios.
Inside package.json
{ "name": "codecept-phantomjs", "version": "0.0.1", "devDependencies": { "grunt": "~0.4.4", "grunt-cli": "~0.1.11", "grunt-run": "^0.1.14", "grunt-contrib-watch": "^0.5.3", "grunt-contrib-less": "^0.9.0", "grunt-contrib-uglify": "^0.2.7", "grunt-exec": "~0.4.3", "bower": "^1.2.8", "phantomjs": "^1.9.7-3" } }
Install all these packages (Note that watch, less, uglify and bower won't specifically be used in this example, but they shall be in an upcoming post).
npm install --save-dev
Install codeception via composer
as follows (You might need to grab a coffee, especially if you already have other dependencies than codeception here.)
composer require "codeception/codeception:*"
In our Gruntfile.js
file, we'll want to setup a task to start PhantomJS in webdriver mode, run codeception, and then close PhantomJS.
module.exports = function(grunt) { var phantomjs = require('phantomjs'); var phantombin = phantomjs.path; grunt.initConfig({ exec: { codecept: { stdout: true, command: [ '"./vendor/bin/codecept" clean', '"./vendor/bin/codecept" run' ].join('&&') } }, run: { phantomjs: { options: { wait: false, quiet: true, ready: /running on port/ }, cmd: phantombin, args: [ '--webdriver=4444' ], } }, watch: { // This is where we’ll trigger tests automagically. } }); grunt.loadNpmTasks('grunt-contrib-less'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-exec'); grunt.loadNpmTasks('grunt-run'); grunt.registerTask('default', []); grunt.registerTask('test', ['run:phantomjs', 'exec:codecept', 'stop:phantomjs']); };
You'll notice I didn't specify where PhantomJS is. When using the NPM package to install PhantomJS, you can use some predefined details about it's location, among some other things - brought in with var phantomjs = require('phantomjs');
and var phantombin = phantomjs.path;
so we don't have to worry about these things like we do with codecept.
Also with calling ./vendor/bin/codecept
I have it double quoted. This will work still on Mac (didn't test Linux, but it's much of a muchness) because Windows tries to execute parts of that command individually and I haven't discovered a nicer way for cross-platform compatibility.
Quickly give us something to test in public_html/index.html
which is simply going to display a blank document, then with javascript inject 'Javascript Test!' into the container div.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Darren Nolan | Testing Codeception with PhantomJS Webdriver and Grunt</title> </head> <body> <div id="container"></div> <script> var container = document.getElementById('container'); container.innerHTML = '<p style="color: red;">Javascript Test!</p>'; </script> </body> </html>
Now lets start bootstrap our codeception tests real quick (we'll get fancy with this later).
codecept bootstrap
Edit our tests/acceptance.suite.yml
details so we use WebDriver and PhantomJS over the PHPBrowser, but we'll leave it in because I'll show you driver switching on a per-test basis (You may not always need PhantomJS to drive your tests, and without rendering pages and javascript PHPBrowser will be quicker. But that's not today's topic!). Take note of the URLs specified, my localhost webserver has many little directories and yours may not (or might be under a different directory etc). So use logics here.
class_name: WebGuy modules: enabled: - WebDriver - PhpBrowser - WebHelper config: WebDriver: url: 'http://localhost/codecept-phantomjs/public_html/' browser: phantomjs capabilities: phantomjs.cli.args: ['--ignore-ssl-errors=true'] PhpBrowser: url: 'http://localhost/codecept-phantomjs/public_html/'
Tell codeception we've changed our driver list and settings which is required when you need additional class helpers brought in (WebDriver for example).
codecept build
FANTASTIC. Now we can test to see this all working (without any tests).
grunt test
You should see the following, (
Done, without errors.
is the 'yay' message we're after).
Great, now we'll start with our super basic task.
codecept generate:cept acceptance Basic
And we'll edit tests/acceptance/BasicCept.php
to the following
<?php $I = new WebGuy($scenario); $I->wantTo('check that javascript is being parsed on the page'); $I->see('Javascript Test!');
And you should see the following
Running "run:phantomjs" (run) task >> phantomjs started Running "exec:codecept" (exec) task Cleaning up C:\public_html\codecept-phantomjs\tests\_log\... Done Codeception PHP Testing Framework v1.8.5 Powered by PHPUnit 3.7.35 by Sebastian Bergmann. Acceptance Tests (1) ------------------------------------------------------------------ Trying to check that javascript is being parsed on the page (BasicCept.php) Ok --------------------------------------------------------------------------------------- Functional Tests (0) ------------------------ --------------------------------------------- Unit Tests (0) ------------------------------ --------------------------------------------- Time: 301 ms, Memory: 8.50Mb OK (1 test, 1 assertion) Running "stop:phantomjs" (stop) task Done, without errors.
We're done! Now every time you want to test your code with PhantomJS, you can just use grunt test
without having to start PhantomJS in another window.
Hi Darren,
Great post – we’re moving from Behat to Codeception at the moment.
Sticking point for us at the moment is driver switching – is there a post in the pipeline with that coming?
Cheers
Sam
I believe you can driver switch, even within the same acceptance tests.
I’ll write something up on the weekend after a bit of a play.
Just realised I spoke about driver switching in the actual post <_< Clearly I've got a project laying about on my computer somewhere where I've already done this. Let me find it. Pretty sure there's two ways of doing it. One is @env and the other is talking to $WebGuy directly and putting in a new driver for him (from memory... which is terrible). I'll quickly check out what I did before and go from there 🙂
The best I’ve been able to reproduce thus far is a second acceptance suite. I’m still trying for alternatives. I have a post that’s pretty much done that shows setting up a second suite for basic plain-text tests. I’ll post that shortly, but I’m still working on the main prize.
Very nice, Darren! Thanks!
Very welcome ^_^