Sunday, 22 December 2013

Unit testing a InputFilter in Zend framework 2

Today i was working on a new resource in my api and i wanted to test my input filter so i didn't have to worry about it causing problems later when i need to do the front end.

So what is a input filter and why is it annoying to test ? Well a input filter is actually pretty much just a configuration of different elements, and testing an array structure is rather pointless. So the two things that we need to test are the element names and it's configuration.

This is my input filter that i am testing... 
/**
 * Class EstimateRequestInputFilter
 */
class EstimateRequestInputFilter extends InputFilter
{
    /**
     * {@Inheritdoc}
     */
    public function init()
    {
        $this->add(
            [
                'name'  => 'name',
                'label' => 'Name',

                'validators' => [
                    [
                        'name' => 'NotEmpty'
                    ],
                ],

                'filters' => [
                    [
                        'name' => 'StringTrim'
                    ]
                ]
            ]
        );

        $this->add(
            [
                'name'  => 'email',
                'label' => 'E-mail',

                'validators' => [
                    [
                        'name'                   => 'NotEmpty',
                        'break_chain_on_failure' => true
                    ],
                    [
                        'name' => 'EmailAddress'
                    ]
                ],

                'filters' => [
                    [
                        'name' => 'StringTrim'
                    ]
                ]
            ]
        );

        $this->add(
            [
                'name'       => 'mobile',
                'label'      => 'Mobile',
                'validators' => [
                    [
                        'name' => 'NotEmpty'
                    ]
                ],

                'filters' => [
                    [
                        'name' => 'StringTrim'
                    ]
                ]
            ]
        );

        $this->add(
            [
                'name'        => 'telephone',
                'label'       => 'Telephone',
                'allow_empty' => true,

                'filters' => [
                    [
                        'name' => 'StringTrim'
                    ]
                ]
            ]
        );

        $this->add(
            [
                'name'        => 'budget',
                'label'       => 'Budget',
                'allow_empty' => true,

                'filters' => [
                    [
                        'name' => 'StringTrim'
                    ]
                ]
            ]
        );

        $this->add(
            [
                'name'        => 'targetDate',
                'label'       => 'Target date',
                'allow_empty' => true,

                'filters' => [
                    [
                        'name' => 'StringTrim'
                    ]
                ]
            ]
        );

        $this->add(
            [
                'name'  => 'projectDescription',
                'label' => 'Project description',

                'validators' => [
                    [
                        'name'                   => 'NotEmpty',
                        'break_chain_on_failure' => true
                    ],
                    [
                        'name'    => 'StringLength',
                        'options' => [
                            'min' => 200
                        ]
                    ]
                ],

                'filters' => [
                    [
                        'name' => 'StringTrim'
                    ]
                ]
            ]
        );
    }
}

If you have a keen eye you will notice that i didn't use a constructor to define the the input filter and that is because i store all my input filters in the InputFilterPluginManager. PLEASE DO THIS!!!

For multiple reasons..
  1. It keeps the service locator clean
  2. It will inject the ValidatorManager and FilterManager so that you can access your custom validators by specifying the name you stored them as. 

And this is my testcase
class EstimateRequestInputFilterTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @var \PHPUnit_Framework_MockObject_MockObject
     */
    private $validator;

    /**
     * @var \Company\InputFilter\Company\EstimateRequestInputFilter
     */
    private $inputFilter;

    protected function setUp()
    {
        $this->inputFilter = new EstimateRequestInputFilter();
        $this->inputFilter->init();
    }

    public function propertyValidationData()
    {
        return [
            ['name', '', [Validator\NotEmpty::IS_EMPTY]],  // Empty
            ['name', ' ', [Validator\NotEmpty::IS_EMPTY]], // Empty, asserting StringTrim
            ['name', 'Antoine', null, 'Antoine'],          // Valid

            ['email', '', [Validator\NotEmpty::IS_EMPTY]],              // Empty
            ['email', ' ', [Validator\NotEmpty::IS_EMPTY]],             // Empty, asserting StringTrim
            ['email', 'foo', [Validator\EmailAddress::INVALID_FORMAT]], // Invalid email address format
            ['email', 'foo@bar.com', null, 'foo@bar.com'],              // Valid

            // todo: add validation for phone numbers from i18n?
            ['mobile', '', [Validator\NotEmpty::IS_EMPTY]],
            ['mobile', ' ', [Validator\NotEmpty::IS_EMPTY]],

            ['telephone', '', null, ''],          // Valid
            ['telephone', 'test ', null, 'test'], // Valid, asserting StringTrim

            ['budget', '', null, ''],          // Valid
            ['budget', 'test ', null, 'test'], // Valid, asserting StringTrim

            ['targetDate', '', null, ''],            // Valid
            ['targetDate', 'test ', null, 'test'],   // Valid, asserting StringTrim

            ['projectDescription', '', [Validator\NotEmpty::IS_EMPTY]],  // Empty
            ['projectDescription', ' ', [Validator\NotEmpty::IS_EMPTY]], // Empty, asserting StringTrim
            ['projectDescription', 'h', [Validator\StringLength::TOO_SHORT]],
            ['projectDescription', str_repeat('h', 200), null, str_repeat('h', 200)]
        ];
    }

    /**
     * @dataProvider propertyValidationData
     *
     * @param string      $property
     * @param string      $value
     * @param array|null  $errors
     * @param string|null $expected
     *
     * @return void
     */
    public function testValidationOfEachProperty($property, $value, array $errors = null, $expected = null)
    {
        // We only validate against the given property
        $this->inputFilter->setValidationGroup([$property]);
        $this->inputFilter->setData([$property => $value]);

        // we expect errors
        if (! empty($errors)) {

            $this->assertFalse($this->inputFilter->isValid());
            $this->assertEquals($errors, array_keys($this->inputFilter->getMessages()[$property]));
        } else {

            $this->assertTrue($this->inputFilter->isValid());
            $this->assertSame($expected, $this->inputFilter->getValue($property));
        }
    }
}

So if you look closely you will see that i'm validating a number of things
  1. Validating that the element with the given name has a certain configuration
  2. Checking that the returned value matches any expected filters (In my case StringTrim)
  3. Checking that a given value with fail with a certain validator message key (Always use keys since the messages can be translated to a different language) 
P.S. This is how i do it, It is no way more correct then other methods. I just thought it would be nice to share!

Thursday, 12 December 2013

Total number of results using PhlyRestfully & collections

Anybody using PhlyRestfully has most likely used a paginated result at one time, and sometimes they need to display the total number of results. It's actually rather easy but a bit different then injecting it into the result body....

What you need to do is create a listener that is attached to the SharedEventManager and inserts a header with the total count. In my case i have selected to use the header "X-total-count".

class InjectPaginationCount implements SharedListenerAggregateInterface
{
    /**
     * @var \Zend\Stdlib\CallbackHandler
     */
    protected $listener;

    /**
     * Attach one or more listeners
     *
     * Implementors may add an optional $priority argument; the SharedEventManager
     * implementation will pass this to the aggregate.
     *
     * @param SharedEventManagerInterface $events
     */
    public function attachShared(SharedEventManagerInterface $events)
    {
        $this->listener = $events->attach('PhlyRestfully\ResourceController', 'getList.post', function(Event $event) {

            /**
             * @var $response \Zend\Http\Response
             * @var $paginator \Zend\Paginator\Paginator
             */
            $response  = $event->getTarget()->getResponse();
            $paginator = $event->getParam('collection')->collection;

            if ($paginator instanceof Paginator) {
                $response->getHeaders()->addHeaderLine('X-total-count', $paginator->getTotalItemCount());
            }
        });
    }

    /**
     * Detach all previously attached listeners
     *
     * @param SharedEventManagerInterface $events
     */
    public function detachShared(SharedEventManagerInterface $events)
    {
        $events->detach('PhlyRestfully\ResourceController', $this->listener);
    }
}


Thursday, 24 October 2013

Dynamic vhosts using a central DNS server

I was inspired by Evans & Robs blogpost about dynamic vhosts using nginx/apache and we used this a fair amount at our office but it's not really useful if you want to quickly share your work with colleagues.

Links to their blogposts
http://blog.evan.pro/how-to-set-up-dynamic-virtual-hosts-for-web-development (Nginx)
http://akrabat.com/computing/automatic-apache-vhosts/ (Apache)

I set about setting up a centralised DNS server that i could use fake TLD's to point to each persons computer. If i would have used dnsmasq instead it would have been a lot easier.... But unluckily i had a fat configuration of bind that i didn't want to migrate to dnsmasq.

Doing this with bind9 step 1

Edit /etc/bind/named.conf.local and add a section zone for each colleague
zone "antoine" {
        type master;
        file "/etc/bind/db.antoine";
};

zone "jonas" {
        type master;
        file "/etc/bind/db.jonas";
};


Save and create a new file for each zone that you have created
; BIND db file for antoine

$TTL 86400

@       IN      SOA     |NAMESERVER DOMAIN|.      |EMAIL DOT DOMAIN|. (
                        2013102401      ; serial number YYMMDDNN
                        28800           ; Refresh
                        7200            ; Retry
                        864000          ; Expire
                        86400           ; Min TTL
                        )

                NS      |NAMESERVER DOMAIN|.

$ORIGIN antoine.

*       3600    A       192.168.0.7


So for us that would be
; BIND db file for antoine

$TTL 86400

@       IN      SOA     router.pmg.se.      spam.pmg.se. (
                        2013102401      ; serial number YYMMDDNN
                        28800           ; Refresh
                        7200            ; Retry
                        864000          ; Expire
                        86400           ; Min TTL
                        )

                NS      router.pmg.se.

$ORIGIN antoine.

*       3600    A       192.168.0.7

I will be frank when i say i have no idea why i have to specify an email in the zone SOA declaration or why it's in that format. I might even have done something terrible wrong.... but it works and it allows me to visit my collegues projects without having to edit my /etc/hosts

Tuesday, 1 October 2013

Why i wish to remove getServiceLocator from AbstractController

Heya good people!

This is the first post on this blog so bare with me! So a few days ago i started a discussion after a PR was made against the ZF2. Here

I especially like the remarks from from a few people
https://github.com/zendframework/zf2/issues/5168#issuecomment-25174848
https://github.com/zendframework/zf2/issues/5168#issuecomment-25232532
https://github.com/zendframework/zf2/issues/5168#issuecomment-25205969

But none of these explicitly tell us why having the ServiceLocatorAwareInterface is an anti-pattern.
So here are my five cents to the discussion.

1, Using the service locator directly within the controller it's really easy to request new services. Which means it's very easy to lose track of the single responsibility principle (SRP) your controller should have.

2, Testing becomes more complicated because you will need to mock the service locator making your tests more fragile. Below you will find the difference between injecting dependencies and use the service locator

First example is using "proper" injection
class ExampleController extends AbstractActionController
{
    public function __construct(FooServiceInterface $fooService, BarServiceInterface $barService)
    {
        $this->fooService = $fooService;
        $this->barService = $barService;
    }

    public function testAction()
    {
        return $this->fooService->foo();
    }

    public function test2Action()
    {
        return $this->barService->bar();
    }
}
And here comes the test
class ExampleControllerTest
{
    protected function setUp()
    { 
        $this->fooService = $this->getMock('FooServiceInterface');
        $this->barService = $this->getMock('BarServiceInterface');

        $this->controller = new ExampleController($this->fooService, $this->barService);
    }
}
Second example is using the service locator anti-pattern
class ExampleController extends AbstractActionController
{
    public function testAction()
    {
        return $this->getServiceLocator()->get('fooService')->foo();
    }

    public function test2Action()
    {
        return $this->getServiceLocator()->get('barService')->bar();
    }
}
And it's test case
class ExampleControllerTest
{
    protected function setUp()
    { 
        $this->fooService = $this->getMock('FooServiceInterface');
        $this->barService = $this->getMock('BarServiceInterface');

        $sm = new ServiceManager();
        $sm->setService('fooService', $this->fooService);
        $sm->setService('barService', $this->barService);

        $this->controller = new ExampleController();
        $this->controller->setServiceLocator($sm);    
    }
}

Note
Both of the setup for our controllers are really quick and dirty and are far from perfect examples of how to properly test a controller.

Conclusion
As you noticed in the second example we also need to setup the ServiceManager and it's not a perfect setup. One could argue that i should modify the ServiceManager that is created by running the bootstrap on the Module. But that means we are doing more then we should be doing in the setUp method.

3, If your lazy like me you love the autocomplete of your favorite IDE but the docblock return param is mixed so your IDE won't be playing nice with you!

Well...... I could just add an annotation

/** @var FooServiceInterface $service */
$service = $this->getServiceLocator()->get('FooService');
return $service->foo();


So my final conclusion
We use the service locator anti-pattern for it's convenience but in the end we still end up writing the same amount of code that is harder to test, less readable and more confusing for new developers that join the project.