Welcome to Abdul Malik Ikhsan's Blog

Testing Hard Dependency with AspectMock

Posted in Teknologi, 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/

Advertisements

PHPUnit: Testing Closure passed to Collaborator

Posted in Teknologi, 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 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.

Testing Lazy Load with ReflectionClass

Posted in Tutorial PHP by samsonasik on January 2, 2015

One of the benefits by using ReflectionClass is when dealing with testing Lazy load. The flow is get the property, use its instance as ReflectionProperty to make it accessible, and set the property value via setValue().
Ok, let’s give a try.
I’ve a “Bar” class that looks like the following :

namespace Samsonasik\TutorialLazyLoading;

/**
 * @author Abdul Malik Ikhsan <samsonasik@gmail.com>
 */
class Bar
{
    /**
     * @var Foo
     */
    private $foo;
    
    /**
     * Get foo property
     * @return Foo
     */
    public function getFoo()
    {
        if (!$this->foo) {
            $this->foo = new Foo;
            return $this->foo;    
        }
        
        return $this->foo;
    }
}

When you call getFoo() in unit test with :

use Samsonasik\TutorialLazyLoading\Bar;
use Samsonasik\TutorialLazyLoading\Foo;

// ...
    protected function setUp()
    {
        $this->bar = new Bar;   
    }
    
    public function testGetFooNotInitializedYet()
    {
        $this->assertInstanceOf(
            Foo::class, /** use 'Samsonasik\TutorialLazyLoading\Foo' for PHP <=5.4 **/
            $this->bar->getFoo()
        );
    }
// ...

, you will get coverage only if (!$this->foo) { block like this :
half-phpunit-test-coverage-on-lazyload

Time to do ReflectionClass in action!, add new test for it’s needed.

use ReflectionClass;
use Samsonasik\TutorialLazyLoading\Bar;
use Samsonasik\TutorialLazyLoading\Foo;

// ...
    protected function setUp()
    {
        $this->bar = new Bar;   
    }
    public function testGetFooNotInitializedYet() { // ...  }
    
    public function testGetFooWithAlreadyInialized()
    {
        $class = new ReflectionClass(
            Bar::class /** use 'Samsonasik\TutorialLazyLoading\Bar' for PHP <=5.4 **/
        );
        $property = $class->getProperty('foo');
        $property->setAccessible(true);
        $property->setValue($this->bar, new Foo);
        
        $this->assertInstanceOf(
            Foo::class, /** use 'Samsonasik\TutorialLazyLoading\Foo' for PHP <=5.4 **/
            $this->bar->getFoo()
        );
    }
// ...   

And yay! You now get fully tested :
full-phpunit-test-coverage-on-lazyload

Note :
As @ocramius suggestion, there is an easier way to do it by calling the method twice ;).

Want to grab it ? You can grab from my repository : https://github.com/samsonasik/TutorialLazyLoading .

References :
1. http://www.mikeyd.com.au/2011/01/20/how-to-use-phps-reflectionclass-to-test-private-methods-and-properties-with-phpunit/

PHPUnit : Unit Testing For PHP

Posted in Teknologi, Tutorial PHP by samsonasik on June 20, 2010

Dalam dunia pemrograman, Unit Testing adalah metode verifikasi perangkat lunak di mana programmer mengetes bahwa suatu unit program layak untuk dipakai. Unit tes biasanya ditulis dan dijalankan oleh programmer untuk memastikan bahwa kode memenuhi desain dan berperilaku sebagaimana diinginkan. Tujuan dari pengujian unit adalah mengisolasi setiap bagian dari program dan menunjukkan bahwa bagian-bagian individu (unit-unit tersebut) adalah benar.  Di dalam procedural programming methodology, sebuah unit adalah sebuah function atau sebuah procedure.

Idealnya, sebuah test kasus dipisahkan dari kasus yang lain. Tapi pada kenyataannya, itu terserah programmer itu sendiri (ketahuan malasnya, xi.xi.xi. )
Ok, let’s bahas tentang PHPUnit. PHPUnit adalah Unit Testing Framework untuk bahasa pemrograman PHP.  Pertama, kita install dulu PHPUnitnya. Petunjuk installnya bisa dilihat di sini . Setelah itu, kita coba buat contoh coding program yang mengexstends kelas di PHPUnit.

<?php
 require_once "PHPUnit/Framework/TestCase.php";

 class testoperasibilangan extends PHPUnit_Framework_TestCase
 {
 //test penambahan...
 public function testTambah()
 {
 $this->assertEquals(11,5+6);
 }
 //test pengurangan...
 public function testPengurangan()
 {
 $this->assertEquals(-1,5-6);
 }
 }
?>

Ok, saatnya mengecek, kita bisa pakai command line untuk mengecek. (sebelumnya, jangan lupa setting path environment ke file execuatable php, ex: set path =c:\webcontainerservernya\bin\php.exe  atau di linux bisa set di /etc/environment )


Dari gambar di atas terlihat bahwa Test OK (berhasil), bagaimana kalau test gagal ? maka akan tampil layaknya seperti gambar berikut.

Demikian, semoga bermanfaat.

Sumber :
http://en.wikipedia.org/wiki/Unit_Testing
http://en.wikipedia.org/wiki/PHPUnit
http://onlamp.com/pub/a/php/2005/12/08/phpunit.html
http://www.phpunit.de/