Welcome to Abdul Malik Ikhsan's Blog

How to Avoid –stderr When Running phpunit for Functional/Integration Testing

Posted in Teknologi, testing by samsonasik on March 3, 2020

When you do a Functional/Integration test with session and/or header relation. It will force you to use --stderr when running phpunit, or it will got error, eg: you’re testing that on logout when session exists as user, page will be redirected to login page with status code 302, and it got the following error:

$ vendor/bin/phpunit test/Integration/LogoutPageTest.php
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.

Logout Page (AppTest\Integration\LogoutPage)
 ✘ Open logout page as auser redirect to login page
   ┐
   ├ Failed asserting that 500 matches expected 302.
   │
   ╵ /Users/samsonasik/www/mezzio-authentication-with-authorization/test/Integration/LogoutPageTest.php:36
   ┴

Time: 155 ms, Memory: 10.00 MB


FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

You can use --stderr option on running it:

$ vendor/bin/phpunit test/Integration/LogoutPageTest.php --stderr
PHPUnit 8.5.2 by Sebastian Bergmann and contributors.

Logout Page (AppTest\Integration\LogoutPage)
 √ Open logout page as auser redirect to login page

Time: 150 ms, Memory: 8.00 MB

OK (1 test, 2 assertions)

or define stderr=true in phpunit.xml configuration:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true"
         testdox="true"
         stderr="true">

    <!-- testsuites, filter, etc config -->   

</phpunit>

Marking all test to be using stderr is a workaround, as not all tests actually require that, eg: unit test doesn’t need that. To avoid it, we can define @runTestsInSeparateProcesses and @preserveGlobalState disabled in the controller class that require that, so, the test class will be like the following:

<?php

declare(strict_types=1);

namespace AppTest\Integration;

use Laminas\Diactoros\ServerRequest;
use Laminas\Diactoros\Uri;
use Mezzio\Authentication\UserInterface;
use PHPUnit\Framework\TestCase;

/**
 * @runTestsInSeparateProcesses
 * @preserveGlobalState disabled
 */
class LogoutPageTest extends TestCase
{
    private $app;

    protected function setUp(): void
    {
        $this->app = AppFactory::create();
    }

    public function testOpenLogoutPageAsAuserRedirectToLoginPage()
    {
        $sessionData                    = [
            'username' => 'samsonasik',
            'roles'    => [
                'user',
            ],
        ];
        $_SESSION[UserInterface::class] = $sessionData;

        $uri           = new Uri('/logout');
        $serverRequest = new ServerRequest([], [], $uri);

        $response = $this->app->handle($serverRequest);
        $this->assertEquals(302, $response->getStatusCode());
        $this->assertEquals('/login', $response->getHeaderLine('Location'));
    }
}

That’s it!

Publish Test Coverage to Codecov from Github Actions

Posted in testing by samsonasik on February 26, 2020

Code Coverage

Github Actions is one of ways to run Continues Integration. For Coverage report, we can use Codecov to publish the coverage result after running and generating test coverage.

For example, you have a Github Repository. You can open https://codecov.io/login and choose “Github”:

After you logged in, you can choose repository, or directly access https://codecov.io/gh/{your github user}/{your github repo}, for example, I use “samsonasik” as user, and “mezzio-authentication-with-authorization” as repository name:

https://codecov.io/gh/samsonasik/mezzio-authentication-with-authorization

On very first, we need to activate Webhook by open https://codecov.io/gh/gh/{your github user}/{your github repo}/settings, for example:

https://codecov.io/gh/samsonasik/mezzio-authentication-with-authorization/settings

Then, we click “Create new webhook” under Github Webhook:

After it done, we can copy “Repository Upload Token”:

by click “Copy” after then token, and back to Github, and save to Secrets section under Your Github Repository Settings with click “Add a new secret”, with eg: named: CODECOV_TOKEN, fill the value with your copied token, and click “Add secret” to save it to be like as follow:

The preparation is done. Now, time to add the github workflow, eg: “.github/workflows/ci-build.yml” at your repository, eg for php package/project and use phpunit, the workflow can be like the following:

name: "ci build"

on:
  pull_request:
  push:
    branches:
      - "master"

jobs:
  build:
    name: PHP ${{ matrix.php-versions }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        php-versions: ['7.2', '7.3', '7.4']
    steps:
      - name: Setup PHP Action
        uses: shivammathur/setup-php@1.8.2
        with:
          extensions: intl
          php-version: "${{ matrix.php-versions }}"
          coverage: pcov
      - name: Checkout
        uses: actions/checkout@v2
      - name: "Validate composer.json and composer.lock"
        run: "composer validate"
      - name: "Install dependencies"
        run: "composer install --prefer-dist --no-progress --no-suggest && composer development-enable"
      - name: "Run test suite"
        run: "vendor/bin/phpunit --coverage-clover=coverage.xml"
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v1
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          file: ./coverage.xml
          flags: tests
          name: codecov-umbrella
          yml: ./codecov.yml
          fail_ci_if_error: true

That’s it. To trigger the Continues Integration, we can push a commit to the repository.

When its succeed (ci build green), we can then display coverage badge, eg at README.md like the following:

[![Code Coverage](https://codecov.io/gh/{your github user}/{your github repo}/branch/master/graph/badge.svg)](https://codecov.io/gh/{your github user}/{your github repo})

that will show:

Code Coverage

Run test and coverage report with ant build in Windows

Posted in Teknologi, testing by samsonasik on July 9, 2019

So, you want to run test and coverage report commands in Windows environment. For example, you want to run kahlan command for test, then make coverage report by istanbul and istanbul-merge.

Based on the ant documentation on exec part, it noted as follow:

The task delegates to Runtime.exec which in turn apparently calls ::CreateProcess. It is the latter Win32 function that defines the exact semantics of the call. In particular, if you do not put a file extension on the executable, only .EXE files are looked for, not .COM, .CMD or other file types listed in the environment variable PATHEXT. That is only used by the shell.
Note that .bat files cannot in general by executed directly. One normally needs to execute the command shell executable cmd using the /c switch.

If you are familiar with ant command, usually, you can create a build.xml file as default build configuration. You can create another build file that you can mention it when run the ant command, eg: build-windows.xml:

$ ant -buildfile build-windows.xml

Your build-windows.xml then can use “cmd” as executable property with /c /path/to/executable-file as argument. The file will like below:

<?xml version="1.0" encoding="UTF-8"?>
<project name="Your Application name" default="build">

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

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

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

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

            <!-- other modules to be tested here -->

    </target>

    <target name="coverage-report"
            description="Run coverage report generation">
            
            <!-- merge coverages for many modules test -->
            <exec executable="cmd" failonerror="true" taskname="istanbul merge">
                <arg line="/c istanbul-merge --out coverage.json coverage/*.json"/>
            </exec>
            
            <!-- make report -->
            <exec executable="cmd" failonerror="true" taskname="istanbul report">
                <arg line="/c istanbul report"/>
            </exec>

    </target>

</project>

With above, when you run the command, if it succeed, it will likely as follow:

$ ant -buildfile build-windows.xml
Buildfile: D:\app\build-windows.xml

kahlan:
   [kahlan]             _     _
   [kahlan]   /\ /\__ _| |__ | | __ _ _ __
   [kahlan]  / //_/ _` | '_ \| |/ _` | '_ \
   [kahlan] / __ \ (_| | | | | | (_| | | | |
   [kahlan] \/  \/\__,_|_| |_|_|\__,_|_| | |
   [kahlan]
   [kahlan] The PHP Test Framework for Freedom, Truth and Justice.
   [kahlan]
   [kahlan] src directory  : D:\app\module\Application\src
   [kahlan] spec directory : D:\app\module\Application\spec
   [kahlan]
   [kahlan] ......................................................  11 / 11 (100%)
   [kahlan]
   [kahlan]
   [kahlan]
   [kahlan] Expectations   : 11 Executed
   [kahlan] Specifications : 0 Pending, 0 Excluded, 0 Skipped
   [kahlan]
   [kahlan] Passed 11 of 11 PASS in 1.831 seconds (using 8Mo)
   [kahlan]
   [kahlan] Coverage Summary
   [kahlan] ----------------
   [kahlan]
   [kahlan] Total: 100.00% (25/25)
   [kahlan]
   [kahlan] Coverage collected in 0.035 seconds

coverage-report:
[istanbul report] Done

build:

BUILD SUCCESSFUL
Total time: 4 seconds

That’s it!

References:

Functional Test Symfony 4 with Kahlan 4

Posted in Symfony 4, testing by samsonasik on January 3, 2018

Yes, there is a bundle for it, but currently not fully work well with kahlan 4 yet. However, we can still use kahlan 4 for it. The simplest way is define Symfony 4 skeleton bootstrap in kahlan config, and use its property at specs, for example, we configure config at kahlan-config.php as follows:

<?php // kahlan-config.php 

use App\Kernel;
use Kahlan\Filter\Filters;
use Symfony\Component\Debug\Debug;
use Symfony\Component\HttpFoundation\Request;

Filters::apply($this, 'bootstrap', function($next) {

    require __DIR__.'/config/bootstrap.php';

    umask(0000);
    Debug::enable();

    $root = $this->suite()->root();
    $root->beforeAll(function () {
        $this->request = Request::createFromGlobals();
        $this->kernel  = new Kernel('test', false);
    });

    return $next();

});

Above settings are minimal, if you need more setup, you can define there. If you didn’t require kahlan/kahlan:^4.0, you can require via composer:

$ composer require --dev kahlan/kahlan:^4.0

Give a try

Let’s try testing a famous /lucky/number from LuckyController. We have the following controller:

<?php // src/Controller/LuckyController.php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

class LuckyController extends Controller
{
    /**
     * @Route("/lucky/number", name="lucky_number")
     */
    public function number()
    {
        $number = mt_rand(0, 100);

        return $this->render('lucky/number.html.twig', [
            'number' => $number,
        ]);
    }
}

And our twig file is:

{# templates/lucky/number.html.twig #}

<h1>Your lucky number is {{ number }}</h1>

We can place test under spec directory at root directory, for its test, we can create a spec/Controller directory:

kahlan.config.php
├── spec
│   └── Controller

Now, we can create the test as follows with make request to the ‘/lucky/number’ page and get its response. We can use toMatchEcho matcher provided with regex to get match random number of mt_rand(0, 100) that printed inside a response html content:

<?php // spec/Controller/LuckyControllerSpec.php

namespace App\Spec\Controller;

describe('LuckyController', function () {

    describe('/lucky/number', function () {

        it('shows lucky number', function () {

            $request = $this->request->create('/lucky/number', 'GET');
            $response = $this->kernel->handle($request);

            expect(function () use ($response) {
                $response->send();
            })->toMatchEcho(
                "#Your lucky number is ([0-9]|[1-8][0-9]|9[0-9]|100)#"
            );

        });

    });

});

Time to run it with command:

$ vendor/bin/kahlan 

We will get the success output:

That’s it 😉

Tagged with:

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 😉

Testing CodeIgniter 4 application with kahlan ^3.0

Posted in CodeIgniter 4, testing, Tutorial PHP by samsonasik on January 14, 2017

This is another post series about CodeIgniter and Kahlan, but now, it will uses CodeIgniter 4. CodeIgniter 4 requires some constants and required classes and functions to be included during application bootstrap to make front controller works.
For example, we need to do functional test App\Controllers\Home controller, this is the kahlan-config.php we need to have:

<?php

use CodeIgniter\CodeIgniter;
use CodeIgniter\Services;
use Config\App;
use Config\Autoload;
use Kahlan\Filter\Filter;

Filter::register('ci.start', function($chain) {
    $root = $this->suite();
    $root->beforeAll(function ($var) {

        define('ENVIRONMENT', 'testing');
        define('BASEPATH',    'system'      . DIRECTORY_SEPARATOR);
        define('APPPATH',     'application' . DIRECTORY_SEPARATOR);
        define('ROOTPATH',    'public'      . DIRECTORY_SEPARATOR);
        define('WRITEPATH',   'writable'    . DIRECTORY_SEPARATOR);
        define('CI_DEBUG',    1);

        require BASEPATH . 'Autoloader/Autoloader.php';
        require APPPATH  . 'Config/Constants.php';
        require APPPATH  . 'Config/Autoload.php';
        require APPPATH  . 'Config/Services.php';

        class_alias('Config\Services', 'CodeIgniter\Services');

        $loader = Services::autoloader();
        $loader->initialize(new Autoload());
        $loader->register();

        require BASEPATH . 'Common.php';
        $app = new App();
        Services::exceptions($app, true)->initialize();

        $var->codeIgniter = new CodeIgniter(
            $app
        );

    });
    return $chain->next();
});
Filter::apply($this, 'run', 'ci.start');

We now can call $this->codeIgniter property in all entire tests.

We can then place the spec under spec/ directory:

.
├── DCO.txt
├── README.md
├── application
│   ├── Controllers
│   │   └── Home.php
├── spec
│   └── Controllers
│       └── HomeDispatchSpec.php

We can apply $_SESSION['argv'] and $_SESSION['argc'] to assign URI routing data:

$_SERVER['argv'] = [
    __FILE__,
    '/', // path
];
$_SERVER['argc'] = 2;

ob_start();
$this->codeIgniter->run();
$actual = ob_get_clean();

expect($actual)->toContain('Welcome to CodeIgniter');

Here is the complete tests:

<?php

namespace AppSpec\Controllers;

describe('Home Dispatch', function () {

    describe('/', function () {

        it('contains "welcome" greeting', function () {

            $_SERVER['argv'] = [
                __FILE__,
                '/', // path
            ];
            $_SERVER['argc'] = 2;

            ob_start();
            $this->codeIgniter->run();
            $actual = ob_get_clean();

            expect($actual)->toContain('Welcome to CodeIgniter');

        });

    });

});

Time to run test:

$ vendor/bin/kahlan --coverage=4 --src=application/Controllers/Home.php
            _     _
  /\ /\__ _| |__ | | __ _ _ __
 / //_/ _` | '_ \| |/ _` | '_ \
/ __ \ (_| | | | | | (_| | | | |
\/  \/\__,_|_| |_|_|\__,_|_| | |

The PHP Test Framework for Freedom, Truth and Justice.

Working Directory: /Users/samsonasik/www/CodeIgniter4

.                                                                   1 / 1 (100%)



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

Passed 1 of 1 PASS in 0.120 seconds (using 4Mo)

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

 \                             1 / 1     100.00%
└── App\                       1 / 1     100.00%
   └── Controllers\            1 / 1     100.00%
      └── Home                 1 / 1     100.00%
         └── Home::index()     1 / 1     100.00%

Total: 100.00% (1/1)

Coverage collected in 0.003 seconds (using an additionnal 16Ko)

Done 😉

Tagged with: ,

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 Hard Dependency with AspectMock

Posted in Teknologi, testing, Tutorial PHP by samsonasik on January 28, 2016

This is another testing legacy application post. Don’t tell your client/other people to refactor, if your job is only to make tests for it, as at some situations, there are reasons to not refactor. You may have situation to test hard dependency that impossible to be mocked and stubbed. There is a library named AspectMock for it, that you can use in PHPUnit, for example.

So, to have it, you can require via composer:

composer require "codeception/aspect-mock:^0.5.5" --dev

For example, you have the following class:

namespace App;

class MyController
{
    public function save()
    {
        $user = new User();
        if (! $user->save()) {
            echo 'not saved';
            return;
        }

        echo 'saved';
    }
}

That bad, huh! Ok, let’s deal to tests it even you don’t really like it. First, setup your phpunit.xml to have ‘backupGlobals=”false”‘ config:

<?xml version="1.0" encoding="UTF-8"?>

<phpunit
    colors="true"
    backupGlobals="false"
    bootstrap="bootstrap.php">
    <testsuites>
        <testsuite name="AppTest">
            <directory suffix=".php">./test</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist addUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./src</directory>
        </whitelist>
    </filter>
</phpunit>

Now, based on config above, you can create bootstrap.php:

include 'vendor/autoload.php';

use AspectMock\Kernel;

$kernel = Kernel::getInstance();
$kernel->init([
    'debug' => true,
    'cacheDir' => __DIR__ . '/data/cache',
    'includePaths' => [__DIR__.'/src'],
]);

Assumption: You have ‘./data/cache’ for saving cache and ‘src/’ for your source code directory, if you use your own autoloader, you can add:

// ...
$kernel->loadFile('YourAutoloader.php');

as the AspectMock documentation mentioned.

Now, time to write the tests:

  1. Preparation
    namespace AppTest;
    
    use PHPUnit_Framework_TestCase;
    use App\MyController;
    use AspectMock\Test as test;
    
    class MyControllerTest extends PHPUnit_Framework_TestCase
    {
        private $myController;
    
        protected function setUp()
        {
            $this->myController = new MyController;
        }
    
        protected function tearDown()
        {
            test::clean(); // remove all registered test doubles
        }
    }
    

  2. write the test cases

class MyControllerTest extends PHPUnit_Framework_TestCase
{
    // ...
    public function provideSave()
    {
        return [
            [true, 'saved'],
            [false, 'not saved'],
        ];
    }

    /**
     * @dataProvider provideSave
     */
    public function testSave($succeed, $echoed)
    {
        // mock
        $userMock = test::double('App\User', ['save' => $succeed]);

        ob_start();
        $this->myController->save();
        $content = ob_get_clean();
        $this->assertEquals($echoed, $content);

        // stub
        $userMock->verifyInvoked('save');
    }
    // ...
}

Done 😉

references:
https://github.com/Codeception/AspectMock
https://twitter.com/grmpyprogrammer/status/642847787713884160
https://littlehart.net/atthekeyboard/2014/12/14/stop-telling-me-to-refactor/

Monkey Patch PHP Quit Statement with Patchwork

Posted in testing, Tutorial PHP by samsonasik on January 18, 2016

If your job is make tests for legacy app that has exit() or die(); everywhere, and you don’t have privilege to refactor them, make tests for them may be hard as the test aborted when the quit statements executed in the tests. To test them, we need to redefine the user-defined functions and methods at runtime, and there is a lib for that, it is named Patchwork.

We can run command:

$ composer require antecedent/patchwork:*

to get the Patchwork dependency.

Let’s see how it can work, let say, we have a class:

namespace App;

class MyClass
{
    public function foo($arg)
    {
        if ($arg === 1) {
            return true;
        }

        exit('app exit.');
    }
}

Seeing the MyClass::foo, we can only tests if the $arg is equal then 1, otherwise, we need to redefine it, and there is a Patchwork\replace() method for it by call like the following:

replace(MyClass::class. '::foo', function($arg) {
    if ($arg === 1) {
        pass();
    }
    return 'app exit.';
});

The pass() method will call original method functionality if $arg === 1 as that not return quit statement, otherwise we redefine to return string with value = ‘app exit’.

We can define in our unit test like this MyClassTest class:

namespace AppTest;

use PHPUnit_Framework_TestCase;
use App\MyClass;
use function Patchwork\pass;
use function Patchwork\replace;
use function Patchwork\undoAll;

class MyClassTest extends PHPUnit_Framework_TestCase
{
    private $myclass;

    protected function setUp()
    {
        replace(MyClass::class. '::foo', function($arg) {
            if ($arg === 1) {
                pass();
            }
            return 'app exit.';
        });

        $this->myclass = new MyClass;
    }

    protected function tearDown()
    {
        undoAll();
    }
}

We can call the replace it in setUp(), and to undo, we can call undoAll() in tearDown(). And now, we can add the tests into MyClassTest tests :

// ...

    public function provideFoo()
    {
        return [
            [1, true],
            [0, 'app exit.'],
        ];
    }

    /**
     * @dataProvider provideFoo
     */
    public function testFoo($arg, $result)
    {
        $this->assertSame($result, $this->myclass->foo($arg));
    }

// ...

References to read:
http://kahlan.readthedocs.org/en/latest/monkey-patching/
http://antecedent.github.io/patchwork/
http://afilina.com/testing-methods-that-make-static-calls/

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 😉

PHPUnit: Testing Closure passed to Collaborator

Posted in Teknologi, testing, Tutorial PHP by samsonasik on October 4, 2015

Yes! Closure is callable, so you can just call __invoke() to the closure returned when test it! This is happen when we, for example, have a class and function that have closure inside it like the following:

class Awesome
{
    public function call($foo)
    {
        return function() use ($foo) {
            return $foo;
        };
    }
}

This can be tested with:

use Awesome;
use PHPUnit_Framework_TestCase;

class AwesomeTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        $this->awesome = new Awesome();
    }

    public function testCall()
    {
        $foo = 'foo';
        $call = $this->awesome->call($foo);
        $this->assertTrue(is_callable($call));

        $invoked = $call->__invoke();
        $this->assertEquals($foo, $invoked);
    }
}

We need an __invoke() call, as the closure never executed before it invoked. So, we need to call that.

On Collaborator Case

The problem is when we have a collaborator, and closure is processed inside the collaborator:

class Awesome
{
    private $awesomeDependency;

    public function __construct(AwesomeDependency $awesomeDependency)
    {
        $this->awesomeDependency = $awesomeDependency;
    }

    public function call($foo)
    {
        $closure = function() use ($foo) {
            return $foo;
        };
        return $this->awesomeDependency->call($closure);
    }
}

and the closure inside call only executed in the AwesomeDependency class:

class AwesomeDependency
{
    public function call($call)
    {
        return $call();
    }
}

Our test can be like the following:

use Awesome;
use AwesomeDependency;
use PHPUnit_Framework_TestCase;

class AwesomeTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        $this->awesomeDependency = $this->prophesize(AwesomeDependency::class);
        $this->awesome     = new Awesome($this->awesomeDependency->reveal());
    }

    public function testCall()
    {
        $foo = 'foo';
        $closure = function() use ($foo) {
            return $foo;
        };

        $this->awesomeDependency
             ->call($closure)
             ->will(function() use ($closure) {
                return $closure->__invoke();
             })
             ->shouldBeCalled();

        $call = $this->awesome->call($foo);
    }
}

As we can see, the $this->awesomeDependency is act as a mock, and calling __invoke() in will() is represent a $closure that already passed to the mock, not the original $closure one, and we will get partial coverage:

closure-call-collaborator1

We know now, it won’t coverable as completed! What we can do? A refactor! But wait, it may be a legacy code, an aggressive refactor may cause problem, so a little but works patch may work for it.

  • Make a $closure as class property, and add mutator and accessor for it.
class Awesome
{
    private $closure;

    // ...

    public function setClosure($closure)
    {
        $this->closure = $closure;
    }

    public function getClosure()
    {
        return $this->closure;
    }

    // ...
}
  • Set $closure property when call call() function:
class Awesome
{
    // ...
    public function call($foo)
    {
        $this->setClosure(function() use ($foo) {
            return $foo;
        });

        return $this->awesomeDependency->call($this->getClosure());
    }
}
  • And in tests, we can now has:
class AwesomeTest extends PHPUnit_Framework_TestCase
{
    // ...
    public function testCall()
    {
        $foo = 'foo';
        $closure = function() use ($foo) {
            return $foo;
        };
        $awesome = $this->awesome;

        $this->awesomeDependency
             ->call($closure)
             ->will(function() use ($awesome) {
                return $awesome->getClosure()->__invoke();
            })
            ->shouldBeCalled();

        $call = $this->awesome->call($foo);
        $this->assertEquals($foo, $call);
    }
}

closure-call-collaborator2

Need a better way? We can replace a closure with an array callback, so, we add additional function that called via call_user_func_array():

class Awesome
{
    public function call($foo)
    {
        return $this->awesomeDependency->call(call_user_func_array(
            [$this, 'onFoo'],
            [$foo]
        ));
    }

    public function onFoo($foo)
    {
        return function() use ($foo) {
            return $foo;
        };
    }
}

And in our tests, we can do:

    public function testCall()
    {
        $foo = 'foo';
        $closure = function() use ($foo) {
            return $foo;
        };
        $awesome = $this->awesome;
        $this->awesomeDependency->call($closure)
             ->will(function() use ($awesome, $foo) {
                 return $awesome->onFoo($foo)->__invoke();
             })
            ->shouldBeCalled();

        $call = $this->awesome->call($foo);
        $this->assertEquals($foo, $call);
    }

And, we now have a fully coverage too:

closure-call-collaborator3

Tagged with: ,

Testing “expects($this->any())” with Prophecy with Spying

Posted in testing, Tutorial PHP by samsonasik on September 27, 2015

Testing method call from Collaborator that may be 0 or any other count with phpunit Framework test case can be done with expects($this->any()) from mock object. If we are using Prophecy for mocking tools, it can be done with spying.
However, the spying itself need checks whenever method is called or not. We need findProphecyMethodCalls() call againsts ObjectProphecy for that.

For example, we have a class with collaborator like the following:

namespace App;

class Awesome
{
    private $awesomeDependency;

    public function __construct(AwesomeDependency $awesomeDependency)
    {
        $this->awesomeDependency = $awesomeDependency;
    }

    public function process($data)
    {
        $rand = rand(1, 2);
        if ($rand === 1) {
            return $this->awesomeDependency->process($data);
        }
        return $data;
    }
}

The rand() usage is just a sample, in real app, we may have a heavy logic and it may fall to not call the collaborator.

The tests can be done like this:

namespace AppTest;

use App\Awesome;
use App\AwesomeDependency;
use PHPUnit_Framework_TestCase;
use Prophecy\Argument;
use Prophecy\Argument\ArgumentsWildcard;

class AwesomeTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        $this->awesomeDependency = $this->prophesize(AwesomeDependency::class);
        $this->awesome     = new Awesome($this->awesomeDependency->reveal());
    }

    public function testProcess()
    {
        $data = [
            'foo' => 'abcdef',
        ];

        // make \Prophecy\MethodProphecy instance
        $methodProphecy = $this->awesomeDependency
                               ->process(Argument::exact($data));
        
        // call method from actual instance
        $this->awesome->process($data);

        $calls = $this->awesomeDependency->findProphecyMethodCalls(
            'process',
            new ArgumentsWildcard([$data])
        );
        $count = count($calls);
        
        // assert how many times it called 
        $methodProphecy->shouldBeCalledTimes($count);
        if ($count) {
            // echoing just to prove
            echo 'Method from collaborator has been called';
            $methodProphecy->shouldHaveBeenCalled();
        } else {
            // echoing just to prove
            echo 'Method from collaborator has not been called';
            $methodProphecy->shouldNotHaveBeenCalled();
        }
    }
}

Of course, it may be can’t be called ‘expecting’ before for something has done, it may be can be called as ‘recording’ what already happen, but by this usage, we can prove if it actually called in actual code.