Welcome to Abdul Malik Ikhsan's Blog

Testing Zend Expressive 2 with kahlan 3

Posted in testing, Tutorial PHP, Zend Framework by samsonasik on March 15, 2017

Zend\Expressive ^2.0 has different default approach for piping and routing middleware which is programmatically way. In this post, I am going to show you how you can test Zend\Expressive ^2.0 application, with assumption, you use its skeleton with kahlan 3.

First, of course, install the Expressive ^2.0 skeleton, for example, install into directory named “expressive2”:

$ composer create-project zendframework/zend-expressive-skeleton:^2.0 expressive2
Installing zendframework/zend-expressive-skeleton (2.0.0)
  - Installing zendframework/zend-expressive-skeleton (2.0.0) Downloading: 100%
Created project in expressive2
> ExpressiveInstaller\OptionalPackages::install
Setting up optional packages
Setup data and cache dir
Removing installer development dependencies

  What type of installation would you like?
  [1] Minimal (no default middleware, templates, or assets; configuration only)
  [2] Flat (flat source code structure; default selection)
  [3] Modular (modular source code structure; recommended)
  Make your selection (2): 3

Now, install kahlan:

$ cd expressive2
$ composer require kahlan/kahlan:^3.1

We are going to need the $app variable inside tests, for example, when testing functionality for each routed middlewares. To simplify and avoid repetitive code, we can register it into kahlan-config.php in root application:

// ./kahlan-config.php
use Kahlan\Filter\Filter;
use Zend\Expressive\Application;

Filter::register('initialize app', function($chain) {

    $root = $this->suite();

    ob_start();

    $root->beforeAll(function ($var) {

        ob_start();

        $var->app = $app
                  = (require 'config/container.php')->get(Application::class);

        require 'config/pipeline.php';
        require 'config/routes.php';

    });

    return $chain->next();

});
Filter::apply($this, 'run', 'initialize app');

By assign $app into “$var->app” like above, the “$app” is accessible from entire tests via “$this->app”, so, we can write test like the following:

// ./src/App/spec/Action/HomePageActionSpec.php
namespace AppSpec\Action;

use Zend\Diactoros\ServerRequest;

describe('HomePageAction', function () {

    describe('/', function () {

        it('contains welcome message', function () {

            $serverRequest = new ServerRequest([], [], '/', 'GET');
            $this->app->run($serverRequest);
            $actual = ob_get_clean();

            expect($actual)->toContain('Welcome to <span class="zf-green">zend-expressive</span>');

        });

    });

});

Now, let’s run the tests:

$ vendor/bin/kahlan --spec=src/App/spec/
            _     _
  /\ /\__ _| |__ | | __ _ _ __
 / //_/ _` | '_ \| |/ _` | '_ \
/ __ \ (_| | | | | | (_| | | | |
\/  \/\__,_|_| |_|_|\__,_|_| | |

The PHP Test Framework for Freedom, Truth and Justice.

Working Directory: /Users/samsonasik/www/expressive2

.                                                                   1 / 1 (100%)



Expectations   : 1 Executed
Specifications : 0 Pending, 0 Excluded, 0 Skipped

Passed 1 of 1 PASS in 0.375 seconds (using 8Mo)

That’s it πŸ˜‰

Functional Test for Zend\Expressive Routed Middleware with Kahlan ^3.0

Posted in testing, Tutorial PHP, Zend Framework by samsonasik on January 13, 2017

You may tried do functional test Zend\Expressive Routed Middleware and end up with “Unable to emit response; headers already sent” error.
This can happen because of during run test, the Test framework itself already run fwrite() or echo to build test report, and make the headers_sent() return true.

To handle that, we can use ob_start(), but since the header is sent in the background, we need to place in both places:

  • test bootstrap
  • before each test

Seriously? Yes! That’s make sure we only get Diactoros response that we use in the buffer to be tested.

Preparation

As usual, we need require kahlan/kahlan:^3.0 in require-dev:

$ composer require --dev kahlan/kahlan:^3.0 --sort-packages

Set Kahlan’s Bootstrap and before each globally

In Kahlan, we can set tests bootstrap and what in all before each test with Kahlan\Filter\Filter in kahlan-config.php, so we can write:

<?php
//kahlan-config.php
use Kahlan\Filter\Filter;

ob_start();
Filter::register('ob_start at each', function($chain) {
    $root = $this->suite();
    $root->beforeEach(function () {
        ob_start();
    });
    return $chain->next();
});
Filter::apply($this, 'run', 'ob_start at each');

Write Spec and Run In Action

Now, if we use Expressive skeleton application, and for example, we need to test App\Action\PingAction routed middleware, we can write spec in spec directory:

.
β”œβ”€β”€ composer.json
β”œβ”€β”€ config
β”œβ”€β”€ data
β”œβ”€β”€ kahlan-config.php
β”œβ”€β”€ public
β”œβ”€β”€ spec
β”‚Β Β  └── App
β”‚Β Β      └── Action
β”‚Β Β          β”œβ”€β”€ PingActionDispatchSpec.php
β”œβ”€β”€ src
β”‚Β Β  └── App
β”‚Β Β      └── Action
β”‚Β Β          β”œβ”€β”€ PingAction.php

As the App\Ping\PingAction is return Zend\Diactoros\Response\JsonResponse which contains “ack” data with time() method call:

return new JsonResponse(['ack' => time()]);

The spec can be the following:

<?php
namespace AppSpec\Action;

use Zend\Diactoros\ServerRequest;
use Zend\Expressive\Application;

describe('PingAction Dispatch', function () {

    beforeAll(function() {
        $container = require 'config/container.php';
        $this->app = $container->get(Application::class);
    });

    describe('/api/ping', function () {

        it('contains json "ack" data', function () {

            allow('time')->toBeCalled()->andReturn('1484291901');

            $serverRequest = new ServerRequest([], [], '/api/ping', 'GET');
            $this->app->run($serverRequest);

            $actual = ob_get_clean();
            expect($actual)->toBe('{"ack":"1484291901"}');

        });

    });

});

The ob_start() will automatically called during test bootstrap and before each test.

Now, we can run the test:

$ vendor/bin/kahlan --coverage=4 --src=src/App/Action/PingAction.php 
            _     _
  /\ /\__ _| |__ | | __ _ _ __
 / //_/ _` | '_ \| |/ _` | '_ \
/ __ \ (_| | | | | | (_| | | | |
\/  \/\__,_|_| |_|_|\__,_|_| | |

The PHP Test Framework for Freedom, Truth and Justice.

Working Directory: /Users/samsonasik/www/expressive

.                                                                   1 / 1 (100%)



Expectations   : 1 Executed
Specifications : 0 Pending, 0 Excluded, 0 Skipped

Passed 1 of 1 PASS in 0.210 seconds (using 7Mo)

Coverage Summary
----------------
                                        Lines           %

 \                                      1 / 1     100.00%
└── App\                                1 / 1     100.00%
   └── Action\                          1 / 1     100.00%
      └── PingAction                    1 / 1     100.00%
         └── PingAction::__invoke()     1 / 1     100.00%

Total: 100.00% (1/1)

Coverage collected in 0.003 seconds (using an additionnal 0o)

Done πŸ˜‰

Merge multiple coverages for Kahlan with istanbul merge

Posted in testing, Tutorial PHP by samsonasik on December 24, 2016

As you may already knew, you can generate HTML coverage report with kahlan with the following command:

$ ./bin/kahlan --src=path/to/src --spec=path/to/spec --istanbul="coverage.json"
$ istanbul report

For multiple src path, with different specs location, as there are multiple coverages, we need to merge them. For example, we have the following application structure:

.
└── module
    β”œβ”€β”€ A
    β”‚Β Β  β”œβ”€β”€ spec
    β”‚Β Β  β”‚Β Β  └── ASpec.php
    β”‚Β Β  └── src
    β”‚Β Β      └── A.php
    └── B
        β”œβ”€β”€ spec
        β”‚Β Β  └── BSpec.php
        └── src
            └── B.php

Prepare dependencies

1. Install the following tools:

2. Install kahlan/kahlan:^3.0

$ composer require kahlan/kahlan:^3.0 --dev --sort-packages

Here is the sample of composer.json:

// composer.json
{
    "name": "samsonasik/kahlan-demo",
    "type": "project",
    "require-dev": {
        "kahlan/kahlan": "^3.0"
    },

    "autoload": {
        "psr-4": {
            "A\\": "module/A/src",
            "B\\": "module/B/src"
        }
    },

    "autoload-dev": {
        "psr-4": {
            "ASpec\\": "module/A/spec",
            "BSpec\\": "module/B/spec"
        }
    },

    "license": "MIT",
    "authors": [
        {
            "name": "Abdul Malik Ikhsan",
            "email": "samsonasik@gmail.com"
        }
    ],
    "config": {
        "bin-dir": "bin"
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}

Write specs

We can write specs under module/{FOLDER}/spec.

Define Tasks on build.xml

We need to register multiple commands in build.xml

  • run bin/kahlan for module/A/src which set coverage target to coverage/coverge-A.json
  • run bin/kahlan for module/B/src which set coverage target to coverage/coverge-B.json
  • run instanbul-merge that merge coverage/coverage*.json to coverage.json
  • run instanbul report

The build.xml can be like the following:

<?xml version="1.0" encoding="UTF-8"?>
<project name="kahlan-demo app" default="build">

    <property name="toolsdir" value="${basedir}/bin/"/>
    <property name="moduledir" value="${basedir}/module/"/>

    <target name="build"
            depends="kahlan,coverage-report"
            description=""/>

    <target name="kahlan"
            description="Run kahlan">

            <!-- A -->
            <exec executable="${toolsdir}kahlan" failonerror="true" taskname="kahlan">
                <arg
                    line="--spec=${moduledir}A/spec/
                    --src=${moduledir}A/src
                    --istanbul=coverage/coverage-A.json
                    "/>
            </exec>
            <!-- A -->

            <!-- B -->
            <exec executable="${toolsdir}kahlan" failonerror="true" taskname="kahlan">
                <arg
                    line="--spec=${moduledir}B/spec/
                    --src=${moduledir}B/src
                    --istanbul=coverage/coverage-B.json
                    "/>
            </exec>
            <!-- B -->

    </target>

    <target name="coverage-report"
            description="Run coverage report generation">

            <!-- merging coverage under coverage/ to coverage.json -->
            <exec executable="istanbul-merge" failonerror="true" taskname="istanbul merge">
                <arg line="--out coverage.json coverage/*.json"/>
            </exec>

            <!-- generate report with use of merged coverages to coverage.json  -->
            <exec executable="istanbul" failonerror="true" taskname="istanbul report">
                <arg line="report"/>
            </exec>

    </target>

</project>

Run tasks

We can run ant command and we will get the following output:

$ ant
Buildfile: /Users/samsonasik/www/kahlan-demo/build.xml

kahlan:
   [kahlan]             _     _
   [kahlan]   /\ /\__ _| |__ | | __ _ _ __
   [kahlan]  / //_/ _` | '_ \| |/ _` | '_ \
   [kahlan] / __ \ (_| | | | | | (_| | | | |
   [kahlan] \/  \/\__,_|_| |_|_|\__,_|_| | |
   [kahlan]
   [kahlan] The PHP Test Framework for Freedom, Truth and Justice.
   [kahlan]
   [kahlan] Working Directory: /Users/samsonasik/www/kahlan-demo
   [kahlan]
   [kahlan] .                                                                   1 / 1 (100%)
   [kahlan]
   [kahlan]
   [kahlan]
   [kahlan] Expectations   : 1 Executed
   [kahlan] Specifications : 0 Pending, 0 Excluded, 0 Skipped
   [kahlan]
   [kahlan] Passed 1 of 1 PASS in 0.074 seconds (using 2Mo)
   [kahlan]
   [kahlan] Coverage Summary
   [kahlan] ----------------
   [kahlan]
   [kahlan] Total: 100.00% (1/1)
   [kahlan]
   [kahlan] Coverage collected in 0.002 seconds (using an additionnal 70Ko)
   [kahlan]
   [kahlan]
   [kahlan]             _     _
   [kahlan]   /\ /\__ _| |__ | | __ _ _ __
   [kahlan]  / //_/ _` | '_ \| |/ _` | '_ \
   [kahlan] / __ \ (_| | | | | | (_| | | | |
   [kahlan] \/  \/\__,_|_| |_|_|\__,_|_| | |
   [kahlan]
   [kahlan] The PHP Test Framework for Freedom, Truth and Justice.
   [kahlan]
   [kahlan] Working Directory: /Users/samsonasik/www/kahlan-demo
   [kahlan]
   [kahlan] .                                                                   1 / 1 (100%)
   [kahlan]
   [kahlan]
   [kahlan]
   [kahlan] Expectations   : 1 Executed
   [kahlan] Specifications : 0 Pending, 0 Excluded, 0 Skipped
   [kahlan]
   [kahlan] Passed 1 of 1 PASS in 0.045 seconds (using 2Mo)
   [kahlan]
   [kahlan] Coverage Summary
   [kahlan] ----------------
   [kahlan]
   [kahlan] Total: 100.00% (1/1)
   [kahlan]
   [kahlan] Coverage collected in 0.001 seconds (using an additionnal 70Ko)
   [kahlan]
   [kahlan]

coverage-report:
[istanbul report] Done

build:

BUILD SUCCESSFUL
Total time: 2 seconds

Now, we have successfully gotten merged coverage results with open coverage/lcov-report/index.html:

kahlan-multiple-cov-merge-result

Unit and Functional testing Zend Framework 3 Controller with Kahlan 3.0

Posted in testing, Tutorial PHP, Zend Framework 2 by samsonasik on October 24, 2016

This post will cover unit and functional testing ZF3 Controller with Kahlan 3.0. For example, you have a ZF3 Skeleton application with an IndexController like the following:

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        return new ViewModel();
    }
}

As usual, we need to require kahlan/kahlan:^3.0 via composer command:

composer require --dev kahlan/kahlan:^3.0 --sort-packages

You can then write the spec. Let’s write our spec inside module/Application/spec like the following structure:

module/Application/
β”œβ”€β”€ config
β”œβ”€β”€ spec
β”‚Β Β  β”œβ”€β”€ Controller
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ IndexControllerDispatchSpec.php
β”‚Β Β  β”‚Β Β  └── IndexControllerSpec.php
β”œβ”€β”€ src
β”‚Β Β  β”œβ”€β”€ Controller
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ IndexController.php

if we are only have the 1 module, named Application module, we can define the spec and src path via kahlan-config.php like the following:

// ./kahlan.config.php
$commandLine = $this->commandLine();
$commandLine->option('spec', 'default', 'module/Application/spec');
$commandLine->option('src', 'default', 'module/Application/src');

Or for multi-modules, we can run parallel command that specify --spec and --src in command like the following:

vendor/bin/kahlan --spec=module/Application/spec --src=module/Application/src

in each iteration. If you’re using ant, you can write a build.xml for tasks definition:

<?xml version="1.0" encoding="UTF-8"?>
<project name="My Website" default="build">
    
    <!-- executable files directory definition -->
    <property name="toolsdir" value="${basedir}/vendor/bin/"/>
    <!-- module directory definition --> 
    <property name="moduledir" value="${basedir}/module/"/>

    <target name="build"
            depends="kahlan"
            description=""/>

    <target name="kahlan"
            description="Run kahlan">
        
        <parallel>    
        
            <!-- Application -->    
            <exec executable="${toolsdir}kahlan" failonerror="true" taskname="kahlan">
                <arg 
                    line="-spec=${moduledir}Application/spec/ 
                    --src=${moduledir}Application/src"/>
            </exec>
            <!-- Application -->
            
            <!-- other modules run test definition go here --> 
        </parallel>
        
    </target>

</project>

Unit testing

Let’s write the unit testing inside spec/Controller/IndexControllerSpec.php:

namespace ApplicationSpec\Controller;

use Application\Controller\IndexController;
use Zend\View\Model\ViewModel;

describe('IndexController', function () {
    
    given('controller', function () {
        
        return new IndexController();
    
    });
    
    describe('->indexAction()', function() {
        
        it('instance of ViewModel', function() {
            
            $actual = $this->controller->indexAction();
            expect($actual)->toBeAnInstanceOf(ViewModel::class);
            
        });
        
    });
    
});

That’s enough for IndexController::indexAction() unit test, nothing complex logic we need to accomodate as it only return the ViewModel instance, so we just need to check if return values is instance of ViewModel.

Functional Testing

Now, we need to make sure if the dispatch response of IndexController::indexAction() by open ‘/’ url that shown by user is the expected result, that show a welcome page, let’s do with spec/Controller/IndexControllerDispatchSpec.php:

namespace ApplicationSpec\Controller;

use Zend\Console\Console;
use Zend\Mvc\Application;

describe('IndexController Dispatch', function () {
    
    // setup the Application
    beforeAll(function () {
        
        Console::overrideIsConsole(false);
        $appConfig = include __DIR__ . '/../../../../config/application.config.php';
        $this->application = Application::init($appConfig);

        $events = $this->application->getEventManager();
        $this->application->getServiceManager()
                          ->get('SendResponseListener')
                          ->detach($events);

    });
    
    // dispatch '/' page tests
    describe('/', function() {
        
        it('contains welcome page', function() {
            
            $request     = $this->application->getRequest();
            
            $request->setMethod('GET');
            $request->setUri('/'); 
            
            // run app with '/' url
            $app =  $this->application->run();
            
            // expect actual response is contain
            // a welcome page
            expect(
                $app->getResponse()->toString()
            )->toContain('<h1>Welcome to <span class="zf-green">Zend Framework</span></h1>');
            
        });
        
    });
    
});

That’s it πŸ˜‰

Testing CodeIgniter application with Kahlan 3.0

Posted in testing, Tutorial PHP by samsonasik on October 19, 2016

Really? Yes, it is testable with kahlan – The PHP Test Framework for Freedom, Truth, and Justice -. Let’s give a try, I am using CodeIgniter 3.1.0 for this example. You can download CodeIgniter from codeigniter.com . For example, we are going to test its Welcome controller.

Setup:
a. require kahlan/kahlan

composer require --dev kahlan/kahlan:^3.0

b. setup minimal autoloading in kahlan-config.php in root CodeIgniter project with Kahlan\Filter\Filter::register() to register its autoloader:

<?php
// ./kahlan.config.php
use Kahlan\Filter\Filter;

define('CI_VERSION', '3.1.0');
define('ENVIRONMENT', 'development');
define('APPPATH', 'application/');
define('VIEWPATH', 'application/views/');
define('BASEPATH', 'system/');

require_once BASEPATH . 'core/Common.php';
function &get_instance()
{
    return CI_Controller::get_instance();
}

Filter::register('ci.autoloader', function($chain) {
    $this->autoloader()->addClassMap([
        // core
        'CI_Controller' =>  BASEPATH . 'core/Controller.php',
        
        // controllers
        'Welcome' => APPPATH . 'controllers/Welcome.php',
    ]);
    return $chain->next();
});
Filter::apply($this, 'namespaces', 'ci.autoloader');

c. Define the spec, we can create spec/controllers directory for placing controller spec:

application/
spec/
└── controllers
    └── WelcomeSpec.php
system/
kahlan-config.php

d. Write the spec:

<?php

describe('Welcome', function () {
    
    describe('->index()', function () {
        
        it('contains welcome message', function() {
            
            $controller = new Welcome();
            
            ob_start();
            $controller->index();
            $actual = ob_get_clean();
            
            expect($actual)->toContain('Welcome to CodeIgniter!');
            
        });
        
    });
    
});

e. run the kahlan command

vendor/bin/kahlan  --coverage=4 --src=application/

and you will get the following output:
kahlan-ci-output

What If we load model into controller ? How to test ?
We can also, For example, you have a model named Welcome_model which check what passed name that will be used in controller:

<?php
// application/models/Welcome_model.php
class Welcome_model extends CI_Model
{
    public function __construct()
    {
        parent::__construct();
    }
    
    public function greeting($name)
    {
        if (trim($name) === '') {
            return 'Hello Guest';
        }
        
        return 'Hello ' . $name;
    }
}

As we need to check uri segment, we need to register new route in application/config/routes.php:

$route['welcome/:name'] = 'welcome/index';

And now, we load in controller:

<?php
// application/controllers/Welcome.php

class Welcome extends CI_Controller
{	
    public function __construct()
    {
         parent::__construct();
		
	    $this->load->model('Welcome_model', 'welcome');
    }
	
    public function index()
    {
	    $greeting = $this->welcome->greeting($this->uri->segment(3));	
	    $this->load->view('welcome_message', ['greeting' => $greeting]);
    }
}

On view ( application/views/welcome_message.php ), we modify the greeting:

<?php // application/views/welcome_message.php ?>
<h1><?php echo $greeting; ?>, Welcome to CodeIgniter!</h1>

At this case, we need a CI_URI::segment() and Welcome_model::greeting() to be stubbed in the spec, so, we need to modify our kahlan-config.php to register CI_URI, CI_Model and its Welcome_model classes:

<?php
// ./kahlan-config.php
use Kahlan\Filter\Filter;

define('CI_VERSION', '3.1.0');
define('ENVIRONMENT', 'development');
define('APPPATH', 'application/');
define('VIEWPATH', 'application/views/');
define('BASEPATH', 'system/');

require_once BASEPATH . 'core/Common.php';
function &get_instance()
{
    return CI_Controller::get_instance();
}

Filter::register('ci.autoloader', function($chain) {
    $this->autoloader()->addClassMap([
        // core
        'CI_Controller' =>  BASEPATH . 'core/Controller.php',
        'CI_URI' =>  BASEPATH . 'core/URI.php',
        'CI_Model' => BASEPATH . 'core/Model.php',
        
        // controllers
        'Welcome' => APPPATH . 'controllers/Welcome.php',
        
        // models
        'Welcome_model' => APPPATH . 'models/Welcome_model.php',
    ]);
    return $chain->next();
});
Filter::apply($this, 'namespaces', 'ci.autoloader');

Then, here is the spec we will need to have:

<?php

use Kahlan\Plugin\Double;

describe('Welcome', function () {
     
    describe('->index()', function () {
         
        it('contains welcome message to specific passed name parameter', function() {
            
            define('UTF8_ENABLED', TRUE); // used by CI_Uri
            
            allow('is_cli')->toBeCalled()->andReturn(false); // to disable _parse_argv call
            
            //  stubs CI_Uri::segment() 
            $uri = Double::instance(['extends' => 'CI_URI']);
            allow($uri)->toReceive('segment')->with(3)->andReturn('samsonasik');
            
            // stubs Welcome_model::greeting()              
            $welcome_model = Double::instance(['extends' => 'Welcome_model']);
            allow($welcome_model)->toReceive('greeting')
                                 ->with('samsonasik')
                                 ->andReturn('Hello samsonasik');
                         
            $controller = new Welcome();
            $controller->uri = $uri;
            $controller->welcome = $welcome_model;
             
            ob_start();
            $controller->index();
            $actual = ob_get_clean();
             
            expect($actual)->toContain('Hello samsonasik, Welcome to CodeIgniter!');
             
        });
         
    });
     
});

As we are stubbing Welcome_model::greeting(), here is the expected output that will be shown on run test:
kahlan-ci-output-with-model-load-in-controller-test.
If we want to make Welcome_model::greeting() coverable, we can create a new spec for testing real Welcome_model::greeting() call.

You wanna grab full sample? I created a repository for it so you can try: https://github.com/samsonasik/ci_310_with_kahlan πŸ˜‰

Done πŸ˜‰

Testing Zend Framework 2 application using phpspec

Posted in testing, Tutorial PHP, Zend Framework 2 by samsonasik on November 8, 2015

If you’re going to start working with new ZF2 application, it is be a good chance to use phpspec for testing tool. We can describe specification and generate code that we already describe. Ok, let’s start with clone ZF2 skeleton application:

$ composer create-project zendframework/skeleton-application:dev-master zfnew

We will start application with “test first”, so, we can remove current module/Application’s classes:

$ cd zfnew
$ rm -rf module/Application/Module.php
$ rm -rf module/Application/src/Application/Controller/IndexController.php

The next step is setup requiring phpspec dependency and its needed extensions:

$ composer config bin-dir bin
$ composer require phpspec/phpspec:~2.3.0 \
                   henrikbjorn/phpspec-code-coverage:~1.0.1 \
                   ciaranmcnulty/phpspec-typehintedmethods:~1.1 --dev

We will use henrikbjorn/phpspec-code-coverage for code coverage generation, and ciaranmcnulty/phpspec-typehintedmethods for typehint generation when running phpspec.

By default, our Application module follow PSR-0 autoloader, so we need to define it in composer.json:

// ...
   "autoload": {
        "psr-0": {
            "Application\\": "module/Application/src/"
        }
    },
// ...

To make it registered in composer’s autoload, we need to run dump-autoload:

$ composer dump-autoload

To point spec to describe Application namespace inside module/Application/src, we need to setup phpspec config under phpspec.yml, we can place it in root zfnew project:

# zfnew/phpspec.yml
suites:
  application_suite:
    namespace: Application
    src_path: module/Application/src/
    spec_path: module/Application

extensions:
  - PhpSpec\Extension\CodeCoverageExtension
  - Cjm\PhpSpec\Extension\TypeHintedMethodsExtension

code_coverage:
  format:
    - html
    - clover
  whitelist:
    - module/Application/src
  output:
    html: coverage
    clover: build/logs/clover.xml

Ok, let’s generate our first spec:

$ bin/phpspec desc Application/Module

We will get output like the following:

desc-1-module-class

We will get generated first spec like the following:

// module/Application/spec/Application/ModuleSpec.php
namespace spec\Application;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class ModuleSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType('Application\Module');
    }
}

And when we run:

$ bin/phpspec run

We will get generated class like this if we choose ‘Y’ answering “Do you want me to create Application\Module for you?” question:

run-1-module-class

And we will get a Module class inside module/Application/src/Application directory:

namespace Application;

class Module
{
}

We need to have more examples to achieve standard Module class, that has getConfig() method, and especially for Application module, we need onBootstrap(MvcEvent $e) method, so we can write examples like the following:

// module/Application/spec/Application/ModuleSpec.php
namespace spec\Application;

use Application\Module;
use PhpSpec\ObjectBehavior;
use Zend\EventManager\EventManager;
use Zend\Mvc\Application;
use Zend\Mvc\MvcEvent;

class ModuleSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType(Module::class);
    }

    function it_return_config()
    {
        $getConfig = $this->getConfig();

        $getConfig->shouldBeArray();
        $getConfig->shouldReturn(
            include __DIR__ . '/../../config/module.config.php'
        );
    }

    function its_bootstrap(MvcEvent $e, $application, $eventManager)
    {
        $application->beADoubleOf(Application::class);
        $eventManager->beADoubleOf(EventManager::class);

        $application->getEventManager()->willReturn($eventManager)->shouldBeCalled();
        $e->getApplication()->willReturn($application)->shouldBeCalled();

        $this->onBootstrap($e);
    }
}

And when run, we will get the following errors:

                                                                                
  Do you want me to create `Application\Module::getConfig()` for you? [Y/n] 
  Y
  Method Application\Module::getConfig() has been created.
                                                                                
  Do you want me to create `Application\Module::onBootstrap()` for you? [Y/n] 
  Y
  
  Method Application\Module::onBootstrap() has been created.
Application/Module                                                                
  18  - it return config
      is_array(null) expected to return true, but it did not.

Application/Module                                                                
  28  - its bootstrap
      some predictions failed:
        Double\Zend\Mvc\MvcEvent\P2:
          No calls have been made that match:
            Double\Zend\Mvc\MvcEvent\P2->getApplication()
          but expected at least one.  Double\Zend\Mvc\Application\P1:
          No calls have been made that match:
            Double\Zend\Mvc\Application\P1->getEventManager()
          but expected at least one.

            33%                                     66%                          3
1 specs
3 examples (1 passed, 2 failed)
708ms

Don’t worry about it, it is normal, we just need to fulfill what already described in code as we have generated code template:

namespace Application;

class Module
{

    public function getConfig()
    {
        // TODO: write logic here
    }

    public function onBootstrap(\Zend\Mvc\MvcEvent $mvcEvent)
    {
        // TODO: write logic here
    }
}

let’s fill it so it looks like:

namespace Application;

use Zend\Mvc\ModuleRouteListener;

class Module
{
    public function getConfig()
    {
        return include __DIR__ . '/../../config/module.config.php';
    }

    public function onBootstrap(\Zend\Mvc\MvcEvent $mvcEvent)
    {
        $eventManager        = $mvcEvent->getApplication()->getEventManager();
        $moduleRouteListener = new ModuleRouteListener();
        $moduleRouteListener->attach($eventManager);
    }
}

To prove, you can re-run bin/phpspec run and everything will be green ;).

Now, let’s create spec for Application\Controller\IndexController:

$ bin/phpspec desc Application/Controller/IndexController

And we can define the IndexControllerSpec:

// module/Application/spec/Application/Controller/IndexControllerSpec.php
namespace spec\Application\Controller;

use Application\Controller\IndexController;
use PhpSpec\ObjectBehavior;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

class IndexControllerSpec extends ObjectBehavior
{
    function let(ViewModel $viewModel)
    {
        $this->beConstructedWith($viewModel);
    }

    function it_is_initializable()
    {
        $this->shouldHaveType(IndexController::class);
    }

    function it_is_extends_abstract_action_controller()
    {
        $this->shouldBeAnInstanceOf(AbstractActionController::class);
    }

    function its_index_action_return_view_model(ViewModel $viewModel)
    {
        $this->indexAction()->shouldReturn($viewModel);
    }
}

We use beConstructedWith(), so, we need to inject ViewModel into controller’s construction. We can run bin/phpspec run and we will get the following code:

// module/Application/src/Application/Controller/IndexController.php
namespace Application\Controller;

class IndexController
{

    public function __construct(\Zend\View\Model\ViewModel $viewModel)
    {
        // TODO: write logic here
    }

    public function indexAction()
    {
        // TODO: write logic here
    }
}

Let’s fulfill the examples as described in spec:

// module/Application/src/Application/Controller/IndexController.php
namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;

class IndexController extends AbstractActionController
{
    private $viewModel;

    public function __construct(\Zend\View\Model\ViewModel $viewModel)
    {
        $this->viewModel = $viewModel;
    }

    public function indexAction()
    {
        return $this->viewModel;
    }
}

We need to build the Controller with Factory, so we can describe the factory:

$ bin/phpspec desc Application/Factory/Controller/IndexControllerFactory

We can write spec examples:

// module/Application/spec/Application/Factory/Controller/IndexControllerFactorySpec.php
namespace spec\Application\Factory\Controller;

use Application\Controller\IndexController;
use Application\Factory\Controller\IndexControllerFactory;
use PhpSpec\ObjectBehavior;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class IndexControllerFactorySpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType(IndexControllerFactory::class);
    }

    function it_is_implements_factory_interface()
    {
        $this->shouldImplement(FactoryInterface::class);
    }

    function it_is_create_indexcontroller(ServiceLocatorInterface $serviceLocator)
    {
        $this->createService($serviceLocator)
             ->shouldReturnAnInstanceOf(IndexController::class);
    }
}

When run bin/phpspec run, we will get generated code:

// module/Application/src/Application/Factory/Controller/IndexControllerFactory.php
namespace Application\Factory\Controller;

class IndexControllerFactory
{

    public function createService(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocatorInterface)
    {
        // TODO: write logic here
    }
}

Let’s modify to fulfill the spec examples:

// module/Application/src/Application/Factory/Controller/IndexControllerFactory.php
namespace Application\Factory\Controller;

use Application\Controller\IndexController;
use Zend\ServiceManager\FactoryInterface;
use Zend\View\Model\ViewModel;

class IndexControllerFactory implements FactoryInterface
{
    public function createService(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocatorInterface)
    {
        $viewModel = new ViewModel();
        return new IndexController($viewModel);
    }
}

So, everything looks good, we can run bin/phpspec run again, and we can get green result again:

run-2-phpspec

We can see the coverage result in coverage/index.html:

cov-phpspec-zf2

Now, to make our ZF2 application still works when call ‘/’ in browser, we can update our module/Application/config/module.config.php:

// ...
    'controllers' => array(
        'factories' => array(
            'Application\Controller\Index' => 'Application\Factory\Controller\IndexControllerFactory'
        ),
    ),
// ...

That’s it πŸ˜‰