Customize your testing platform with events, plugins, reporters, scopes, and more!
Austin Morris / @austinsmorris
Slides: https://austinsmorris.github.io/extending-peridot
<?php
describe('A thing', function () {
// I am a suite!
beforeEach(function () {
// Somebody set up us the bomb!
});
it('should do a thing', function () {
// I am a test!
});
// more it()...
afterEach(function () {
// Tear down!
});
});
describe('A thing', function () {
beforeEach(function () {
// Somebody set up us the bomb!
});
context('in this context', function () {
// I am another suite!
beforeEach(function () {
// I only run in this context
});
it('should do a very specific thing', function () {
// Both beforeEach() functions were executed before me!
});
});
});
"The Console component eases the creation of beautiful and testable command line interfaces."
Abstractions for:
Configure your PHP tests with PHP!
Even this is configurable:
$ vendor/bin/peridot -c foo/bar.php
$ vendor/bin/peridot --configuration foo/bar.php
<?php
use Evenement\EventEmitterInterface;
/**
* Configure peridot.
*
* @param EventEmitterInterface $eventEmitter
*/
return function (EventEmitterInterface $eventEmitter) {
// Everything that is cool and rad goes here!
};
Peridot has a single application event manager driving and providing access to the entire testing lifecyle.
Événement
interface EventEmitterInterface
{
public function on($event, callable $listener);
public function once($event, callable $listener);
public function removeListener($event, callable $listener);
public function removeAllListeners($event = null);
public function listeners($event);
public function emit($event, array $arguments = []);
}
Set the default path:
use Evenement\EventEmitterInterface;
use Peridot\Console\Environment;
return function (EventEmitterInterface $eventEmitter) {
$eventEmitter->on('peridot.start', function (Environment $environment) {
$environment->getDefinition()->getArgument('path')->setDefault('specs');
});
};
use Evenement\EventEmitterInterface;
use Peridot\Core\Test;
return function (EventEmitterInterface $eventEmitter) {
$eventEmitter->on('test.passed', function (Test $test) use ($eventEmitter) {
$eventEmitter->emit('wackyEvent', [$test]);
});
$eventEmitter->on('wackyEvent', function (Test $test) {
// do something wacky!
});
};
class MyPlugin
{
protected $emitter;
public function __construct(EventEmitterInterface $emitter)
{
$this->emitter = $emitter;
$this->listen();
}
protected function listen()
{
$this->emitter->on('suite.start', [$this, 'onSuiteStart']);
}
public function onSuiteStart(Suite $suite)
{
// do something when a suite starts
}
}
use Evenement\EventEmitterInterface;
return function (EventEmitterInterface $eventEmitter) {
new MyPlugin($eventEmitter);
};
use Evenement\EventEmitterInterface;
use Peridot\Plugin\Yo\YoPlugin;
return function (EventEmitterInterface $emitter) {
$yoPlugin = YoPlugin::register(
$emitter,
"your-yo-token",
['USER1', 'USER2'],
'http://linktobuild.com'
);
$yoPlugin->setBehavior(YoPlugin::BEHAVIOR_ON_FAIL);
};
use Evenement\EventEmitterInterface;
use Peridot\Plugin\Watcher\WatcherPlugin;
return function(EventEmitterInterface $emitter) {
$watcher = new WatcherPlugin($emitter);
$watcher->track(__DIR__ . '/src');
};
use Evenement\EventEmitterInterface;
use Peridot\Concurrency\ConcurrencyPlugin;
return function (EventEmitterInterface $emitter) {
new ConcurrencyPlugin($emitter);
};
$ vendor/bin/peridot --concurrent
class DotReporterPlugin
{
// ...
protected function listen()
{
$this->emitter->on('peridot.reporters', [$this, 'onPeridotReporters']);
}
public function onPeridotReporters(InputInterface $input, ReporterFactory $reporters)
{
$reporters->register('dot', 'dot matrix', 'Peridot\Reporter\Dot\DotReporter');
}
}
namespace Peridot\Reporter\Dot;
use Peridot\Reporter\AbstractBaseReporter;
class DotReporter extends AbstractBaseReporter
{
public function init()
{
$this->eventEmitter->on('test.passed', [$this, 'onTestPassed']);
$this->eventEmitter->on('test.failed', [$this, 'onTestFailed']);
$this->eventEmitter->on('test.pending', [$this, 'onTestPending']);
$this->eventEmitter->on('runner.end', [$this, 'onRunnerEnd']);
}
}
use Peridot\Reporter\Dot\DotReporterPlugin;
return function(EventEmitterInterface $emitter) {
new DotReporterPlugin($emitter);
};
$ vendor/bin/peridot -r dot
use Evenement\EventEmitterInterface;
use Peridot\Reporter\CodeCoverageReporters;
return function (EventEmitterInterface $eventEmitter) {
(new CodeCoverageReporters($eventEmitter))->register();
$eventEmitter->on('code-coverage.start', function ($reporter) {
$reporter->addDirectoryToWhitelist(__DIR__ . '/src');
});
};
$ vendor/bin/peridot -r html-code-coverage --code-coverage-path coverage/
class MyScopePlugin
{
// ...
protected function listen()
{
$this->emitter->on('suite.start', [$this, 'onSuiteStart']);
}
public function onSuiteStart(Suite $suite)
{
$suite->getScope()->peridotAddChildScope(new MyScope());
}
}
class MyScope
{
// tests can access $this->thingy
public $thingy;
// tests can call $this->doThingyMagic()
public function doThingyMagic()
{
// magic happens!
}
}
use Evenement\EventEmitterInterface;
return function (EventEmitterInterface $eventEmitter) {
new MyScopePlugin($eventEmitter);
};
describe('A thing', function () {
$scope = new MyScope();
$this->peridotAddChildScope($scope);
it('should do a thing', function() {
$this->doThingyMagic()
});
}
describe('A thing', function() {
beforeEach(function () {
// get a \Prophecy\Prophet
$prophet = $this->getProphet()
)};
});
describe('My\Thing', function() {
it('should be a Thing', function() {
assert($this->subject->reveal() instanceof Thing);
});
});
describe('My API', function() {
describe('/my-route', function() {
it('should return data', function() {
$this->client->request('GET', '/my-route');
$data = json_decode($this->client->getResponse()->getContent(), true);
// assert($data blah blah blah...
});
});
});
use Evenement\EventEmitterInterface;
use Peridot\Plugin\Doctrine\DoctrinePlugin;
use Peridot\Plugin\Doctrine\EntityManager\EntityManagerService;
return function (EventEmitterInterface $eventEmitter) {
new DoctrinePlugin($eventEmitter, new EntityManagerService());
};
beforeEach(function () {
// I can create an entity manager!
$this->entityManager = $this->createEntityManager();
$this->repository = new MyRepository($this->entityManager);
});
Customize your entity manager creation
use Doctrine\DBAL\Types\Type;
Type::addType('datetimeutc', 'ASM\Doctrine\DBAL\Types\DateTimeUTCType');
$factory = function () {
// do something that creates an entity manager ..
return $entityManager;
};
$service = (new EntityManagerService())->setEntityManagerFactory($factory);
new DoctrinePlugin($eventEmitter, $service);
Copying is faster than creating
use Nelmio\Alice\Fixtures;
use Peridot\Plugin\Doctrine\EntityManager\SchemaService;
// create the initial sqlite db used for testing
$eventEmitter->on('runner.start', function () use ($factory) {
$entityManager = $factory();
$schemaService = new SchemaService($entityManager);
$schemaService->dropDatabase()->createDatabase();
Fixtures::load([__DIR__ . '/path/to/fixture.yml'], $entityManager);
// load more fixtures...
copy(__DIR__ . '/tmp/db.db', __DIR__ . '/tmp/clean.db');
});
// create a clean copy of the db before an entity manager is created
$eventEmitter->on('doctrine.entityManager.preCreate', function () {
copy(__DIR__ . '/tmp/clean.db', __DIR__ . '/tmp/db.db');
});
An assertion library to complement Peridot.
$ composer require peridot-php/leo:~1.0
use Peridot\Leo\Interfaces\Assert;
$assert = new Assert();
$assert->typeOf('string', 'hello', 'is string');
$assert->operator(5, '<', 6, 'should be less than');
$assert->isResource(tempfile());
$assert->throws(function() {
throw new Exception("exception");
}, 'Exception');
expect([1,2,3])->to->have->length(3);
expect($name)->to->be->a('string')
expect($object)->to->have->property('name', 'brian');
expect(function() {})->to->satisfy('is_callable');
expect(5)->to->be->above(4)
expect($num)->to->be->within(5, 10);
expect('hello')->to->match('/^he/');
use Evenement\EventEmitterInterface;
use Peridot\Leo\HttpFoundation\LeoHttpFoundation;
use Peridot\Leo\Leo;
return function (EventEmitterInterface $eventEmitter) {
Leo::assertion()->extend(new LeoHttpFoundation());
};
expect($response)->to->allow(['POST', 'GET']);
expect($response)->to->have->status(200);
expect($response)->json->to->have->property('name');