How to Unit Test a CakePHP Console (Shell) Command — An Example

Wow, it sure took me a long time to find an example of how to write a unit test for my CakePHP console (shell) commands. There’s no how-to in the CakePHP documentation, and the only blog post I could find was this thing from 2009 and a correspondingly outdated version of the software.

I eventually found some code on github that Mark Story himself had written for a side project, and using that I was able to put together a bare-bones example of how to write a unit test for a CakePHP console (shell) command.

I deliberately wrote a really dumb shell to test because I just wanted to see that a test could be written. I figured, once I had the pattern I could write more sophisticated tests.

Anyway, if you’re stuck like I was, please follow along.

Step 1: create a really dumb console

Create a file called ThingyShell.php in this folder of your CakePHP application:  /app/Console/Command/

These are the contents. (Later, we’ll test the method called getFour().):

<?php
class ThingyShell extends Shell {
    public function main()
    {
        $four = $this->getFour();
        $this->out($four);
    }
    
    public function getFour()
    {
        return "4";
    }
}

Step 2: make sure your console runs

I wanted to make sure I didn’t include any dumb errors in my dumb shell, so I  ran the console locally to ensure it works:

C:\[...etc...]\app>cake Thingy


Welcome to CakePHP v2.7.0-dev Console
---------------------------------------------------------------
App : app
Path: C:\[...etc...]\app\
---------------------------------------------------------------
4

Step 3: create a test file

Create a file called ThingyTest.php in this folder of your CakePHP application:  /app/Test/Case/Console/Command/

These are the contents:

<?php
App::uses('ConsoleOutput', 'Console');
App::uses('ConsoleInput', 'Console');
App::uses('Shell', 'Console');
App::uses('ThingyShell', 'Console/Command');

class ThingyTest extends CakeTestCase
{
    public function setUp()
    {
        parent::setUp();
        $out = $this->getMock('ConsoleOutput', array(), array(), '', false);
        $in = $this->getMock('ConsoleInput', array(), array(), '', false);
        
        $this->Thingy = $this->getMock('ThingyShell', 
            array('in', 'err', 'createFile', '_stop', 'clear'),
            array($out, $out, $in)
            );
    }
    
    public function testGetFour()
    {
        $expected = "5";
        $actual = $this->Thingy->getFour();
        $this->assertEquals($expected, $actual);
    }
    
    public function tearDown()
    {
        parent::tearDown();
        unset($this->Thingy);
    }
}

You should now be able to navigate to your tests and run Console / Command / Thingy.

cakephp-test-console

You should see a nice failure message like this one:

cakephp-test-results

A few comments

The getMock() method comes from PHPUnit, not from CakePHP. Mr. Story explains the parameters on a blog post from 2010.

Essentially, the ThingyShell getMock is saying: replace the methods called ‘in’, ‘err’, ‘createFile’, ‘_stop’, and ‘clear’ with the variables offered on the next line. And those variables are empty mocks of ConsoleOutput and ConsoleInput.

Any methods not replaced will run on your real ThingyShell object.

Once you’ve got this proof of concept working, you should be able to expand on it to write real unit tests on your real console shells.

 

 

Advertisements
This entry was posted in CakePHP, Testing. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s