March 31, 2013

ZF2 Unit test album module returns routing issue

Question by Beniston

I am trying out the phpunit in the Zf2 album module. I encountered an error which states about routing.

Below is the debug information. It says ‘Route with name “album” not found’, but when I checked module.config.php in the album module folder, I see that is correctly set and in the browser the redirection to that route is working fine.

AlbumControllerAlbumControllerTest::testDeleteActionCanBeAccessed
ZendMvcRouterExceptionRuntimeException: Route with name "album" not found
D:wwwzend2vendorzendframeworkzendframeworklibraryZendMvcRouterSimpleRouteStack.php:292
D:wwwzend2vendorzendframeworkzendframeworklibraryZendMvcControllerPluginUrl.php:88
D:wwwzend2vendorzendframeworkzendframeworklibraryZendMvcControllerPluginRedirect.php:54
D:wwwzend2moduleAlbumsrcAlbumControllerAlbumController.php:80
D:wwwzend2vendorzendframeworkzendframeworklibraryZendMvcControllerAbstractActionController.php:87
D:wwwzend2vendorzendframeworkzendframeworklibraryZendEventManagerEventManager.php:468
D:wwwzend2vendorzendframeworkzendframeworklibraryZendEventManagerEventManager.php:208
D:wwwzend2vendorzendframeworkzendframeworklibraryZendMvcControllerAbstractController.php:108
D:wwwzend2testsmoduleAlbumsrcAlbumControllerAlbumControllerTest.php:35
C:wampbinphpphp5.4.3phpunit:46

I understand that the issue in AlbumController.php line 80 is

return $this->redirect()->toRoute('album');

But not sure why it is not working. Any one has encountered and overcome such issues?

Answer by dave b.

I hope it will save approx. 30 minutes of searching in the zend framework 2 code:

class AlbumControllerTest extends PHPUnit_Framework_TestCase
{

//...

    protected function setUp()
    {
        $bootstrap        = ZendMvcApplication::init(include 'config/application.config.php');
        $this->controller = new AlbumController();
        $this->request    = new Request();
        $this->routeMatch = new RouteMatch(array('controller' => 'index'));
        $this->event      = $bootstrap->getMvcEvent();

        $router = new ZendMvcRouterSimpleRouteStack();
        $options = array(
                    'route'    => '/album[/:action][/:id]',
                    'constraints' => array(
                        'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
                        'id'     => '[0-9]+',
                    ),
                    'defaults' => array(
                        'controller' => 'AlbumControllerAlbum',
                        'action'     => 'index',
                    ),
            );
        $route = ZendMvcRouterHttpSegment::factory($options);

        $router->addRoute('album', $route);

        $this->event->setRouter($router);
        $this->event->setRouteMatch($this->routeMatch);
        $this->controller->setEvent($this->event);
        $this->controller->setEventManager($bootstrap->getEventManager());
        $this->controller->setServiceLocator($bootstrap->getServiceManager());
    }

}

Answer by Starx

Actually the easy way is to get the config data from the service manager:

$config = $serviceManager->get('Config');

Full code for the function setUp():

protected function setUp() {
    $serviceManager = Bootstrap::getServiceManager();
    $this -> controller = new AlbumController();
    $this -> request = new Request();
    $this -> routeMatch = new RouteMatch(
        array(
            'controller' => 'index',
        )
    );
    $this -> event = new MvcEvent();
    $config = $serviceManager->get('Config');
    $routerConfig = isset($config['router']) ? $config['router']  : array();
    $router = HttpRouter::factory($routerConfig);
    $this -> event -> setRouter($router);
    $this -> event -> setRouteMatch($this -> routeMatch);
    $this -> controller -> setEvent($this -> event);
    $this -> controller -> setServiceLocator($serviceManager);
}
December 20, 2012

Call to undefined method PHP_CodeCoverage_Filter::getInstance()

Question by beanland

I’ve got a fresh copy of PHPUnit installed on my system (Ubuntu 11), but whenever I type phpunit in the console I get the following error:

PHP Fatal error: Call to undefined method PHP_CodeCoverage_Filter::getInstance() in /usr/bin/phpunit on line 39

I have PHPUnit’s code coverage installed, as far as I know:

>sudo pear install phpunit/PHP_CodeCoverage

phpunit/PHP_CodeCoverage is already installed and is the same as the released version 1.1.1

install failed

Why am I getting this error and how can I fix it?

Answer by David Harkness

The executable script that loads PHPUnit must not have been updated when going to 3.6.x. Reinstall it.

sudo pear uninstall phpunit/PHPUnit
sudo pear install phpunit/PHPUnit

If this doesn’t work, make sure PEAR itself is up-to-date.

Answer by Starx

For some, Anthony’s solution will not work fully because of the Unknown remote channel: pear.symfony.com or phpunit/PHPUnit requires package "channel://pear.symfony.com/Yaml".

SO here is the upgraded solution that solves this:

sudo apt-get remove phpunit

sudo pear channel-discover pear.phpunit.de

sudo pear channel-discover pear.symfony-project.com

sudo pear channel-discover components.ez.no

sudo pear channel-discover pear.symfony.com

sudo pear update-channels

sudo pear upgrade-all

sudo pear install pear.symfony.com/Yaml

sudo pear install --alldeps phpunit/PHPUnit

sudo pear install --force --alldeps phpunit/PHPUnit
August 13, 2012

Is it bad to mock the object being tested in a unit test?

Question by user1566921

Here is the class I am unit testing. Currently I am testing the doSomething function:

class FooClass {
  public function doSomething( $user ) {
    $conn = $this->getUniqueConnection( $user->id );
    $conn->doSomethingDestructive();
  }

  private function getUniqueConnection( $id ) {
    return new UniqueConnection( $id );
  }
}

As you can see, the doSomething function gets a new instance of UniqueConnection (a class I am not testing here) based on a property of the argument it receives. The problem is that UniqueConnection:: doSomethingDestructive method is something I cannot call during tests due to its… destructiveness. So I would like to stub/mock the UniqueConnection rather than use a real one.

I don’t see any way to inject my mocked UniqueConnection. I would make the UniqueConnection a constructor argument for FooClass but, as you can see, a new one gets created based on the parameter to the doSomething function and all the unique ids it may be called with are not known ahead of time.

My only option that I can see is to test a mock of FooClass instead of FooClass itself. Then I would replace the getUniqueConnection function with one that returns a mock/stub. This seems bad to test an mock, but I don’t see any way to achieve what I am after otherwise. UniqueConnection is a third party vendor library and cannot be modified.

Answer by Starx

Creating classes in a way that it can support different modes of execution is very important in some cases. One of these cases is what you are asking for.

Create your classes to support various modes. For example

Class Connection {
    private $mode;

    public function setMode($mode) {
         $this -> $mode = $mode;
    }
}

Now, your doSomethingDestructive can act as per the execution mode.

public function doSomethingDestructive() {
    if($this -> mode === "test") { //if we are in a test mode scenario
        //Log something
        // Or just do some logging and give a message
    } else {
        // do what it was suppose to do
    }
}

Next time, when you are testing the class, you dont have to worry about that destructive function doing something destruction accidentally.

  public function doSomething( $user ) {
    $conn = $this->getUniqueConnection( $user->id );
    $conn -> setMode("test"); //Now we are safe
    $conn->doSomethingDestructive(); //But the Testing is still being Ran
  }
...

Please fill the form - I will response as fast as I can!