Writing unit tests for your craft plugin has not been straight forward out of the gate but P&T is taking steps to fully integrate unit testing and have already added the basic necessities to do so.
@craft = /path/to/site/craft
@craftTests = @craft/app/tests
@craftVendor = @craft/app/vendor
@craftPlugins = @craft/plugins
@pluginTestDir = @craft/plugins/yourplugin/tests
/path/to/site/craft
- /app
- /tests
- /vendor
- /plugins
- plugin
- tests
- PHP 5.3.2+ is properly installed and can be called from the command line
<?php
namespace Craft;
use \Mockery as m;
class PluginTest extends BaseTest
{
protected $config;
protected $service;
public function setUp()
{
$this->config = m::mock('Craft\ConfigService');
$this->config->shouldReceive('getIsInitialized')->andReturn(true);
$this->config->shouldReceive('usePathInfo')->andReturn(true)->byDefault();
$this->config->shouldReceive('get')->with('usePathInfo')->andReturn(true)->byDefault();
$this->config->shouldReceive('get')->with('cpTrigger')->andReturn('admin')->byDefault();
$this->config->shouldReceive('get')->with('pageTrigger')->andReturn('p')->byDefault();
$this->config->shouldReceive('get')->with('actionTrigger')->andReturn('action')->byDefault();
$this->config->shouldReceive('get')->with('translationDebugOutput')->andReturn(false)->byDefault();
$this->config->shouldReceive('getLocalized')->with('loginPath')->andReturn('login')->byDefault();
$this->config->shouldReceive('getLocalized')->with('logoutPath')->andReturn('logout')->byDefault();
$this->config->shouldReceive('getLocalized')->with('setPasswordPath')->andReturn('setpassword')->byDefault();
$this->config->shouldReceive('getCpLoginPath')->andReturn('login')->byDefault();
$this->config->shouldReceive('getCpLogoutPath')->andReturn('logout')->byDefault();
$this->config->shouldReceive('getCpSetPasswordPath')->andReturn('setpassword')->byDefault();
$this->config->shouldReceive('getResourceTrigger')->andReturn('resource')->byDefault();
$this->setComponent(craft(), 'config', $this->config);
$this->setEnvironment();
$this->loadServices();
}
public function testGetRequestIp()
{
$this->assertEquals($_SERVER['REMOTE_ADDR'], '127.0.0.1');
}
protected function setEnvironment()
{
$this->settings = m::mock('Craft\Model');
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
}
protected function loadServices()
{
require_once __DIR__ . '/../services/PluginService.php';
$this->service = new PluginService();
}
protected function inspect($data)
{
fwrite(STDERR, print_r($data));
}
}
This is an example to run from the command line but do note that there are other ways to do it.
/path/to/php -c /path/to/php.ini @craftVendor/phpunit/phpunit/phpunit.php --bootstrap @craftTests/bootstrap.php --configuration @craftTests/phpunit.xml @pluginTestDir
These commands should be executed as a single command with exactly one space between each step.
php -c /etc/php5/apache2/php.ini
/path/to/site/craft/vendor/phpunit/phpunit/phpunit.php
or which phpunit
--bootstrap /path/to/site/craft/app/tests/bootstrap.php
--configuration /path/to/site/craft/app/tests/phpunit.xml
/path/to/site/craft/plugins/yourplugin/tests
The biggest issues I've come across during testing have to do with bootstrapping dependencies.
Some issues are really hard to diagnose and explain and in effort to keep this short... I figured that rather than explaining every single issue I've found, I'd just post what is currently working for me and other's I've helped and if you can't get things running just post a comment here and we can sort it out together.
Selvin Ortiz
Lead Developer
Barrel Strength Design
Thanks for pointing out the typo John.
The sample test was a last minute thing and the point was to illustrate what you may write in your pluging so I think it's totally out of context and the paths were totally relying on your plugin files like services being present.
What I think I'll do is create a simple skeleton plugin an turn this gist into a full repo so that I can provide more context and a full walkthrough so that others can use as reference and contribute their findings in a more github like way!
I'll be reviewing/formalizing this tonight to make it easier to get started with.
Thanks for the feedback bro!