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

I assume the following

  • NodeJS / NPM Installed.
  • Composer installed globally, or where I call composer by itself, have composer.phar downloaded already and use php composer.phar
  • Codeception installed globally, or where I call codecept by itself, use php 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;

        exec: {
            codecept: {
                stdout: true,
                command: [
                    '"./vendor/bin/codecept" clean',
                    '"./vendor/bin/codecept" run'
        run: {
            phantomjs: {
                options: {
                    wait: false,
                    quiet: true,
                    ready: /running on port/
                cmd: phantombin,
                args: [
        watch: {
            // This is where we’ll trigger tests automagically.


    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">
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Darren Nolan | Testing Codeception with PhantomJS Webdriver and Grunt</title>
  <div id="container"></div>

    var container = document.getElementById('container');
    container.innerHTML = '<p style="color: red;">Javascript Test!</p>';

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
        - WebDriver
        - PhpBrowser
        - WebHelper
            url: 'http://localhost/codecept-phantomjs/public_html/'
            browser: phantomjs
                phantomjs.cli.args: ['--ignore-ssl-errors=true']
            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

$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\...
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.