PHPSPEC TIPS & TRICKS PHPERS SILESIA 24.05.2016
PHPSPEC TIPS & TRICKS
PHPERS SILESIA 24.05.2016
Filip Golonka
Team leader & developer at Schibsted Tech Polska
FilipGolonka
http://github.com/filipgolonka
Kraków
PHPSPEC - TIPS&TRICKS
PHPSPEC
▸ SpecBDD Framework for PHP
▸ A php toolset to drive emergent design by specification.
▸ phpspec is a tool which can help you write clean and working PHP code using behaviour driven development
& community
PHPSTORM SUPPORT
namespace spec\AppBundle\Api; use PhpSpec\ObjectBehavior; use AppBundle\Api\ListParameters; /** * @mixin ListParameters */class ListParametersSpec extends ObjectBehavior{ function it_is_initializable() { $this->shouldHaveType(ListParameters::class); } }
NOT COMMON MATCHERS
https://github.com/yvoyer/phpspec-cheat-sheet
PHPSPEC - TIPS&TRICKS
ARRAYS & STRINGS MATCHERS
▸ $this->getArrayValue()->shouldContain('Jane Smith');
▸ $this->getArrayValue()->shouldHaveKeyWithValue('key', 'value');
▸ $this->getArrayValue()->shouldHaveKey(‚key');
▸ $this->getStringValue()->shouldContain('value');
▸ $this->getStringValue()->shouldStartWith('value');
▸ $this->getStringValue()->shouldEndWith('value');
▸ $this->getStringValue()->shouldMatch(‚/pattern/');
PHPSPEC - TIPS&TRICKS
BOOLEAN MATCHERSclass Event { private $active; public function isActive(): bool { return $this->active; } }
class EventSpec extends ObjectBehavior{ function it_is_active() { $this->setActive(); $this->shouldBeActive(); // or $this->isActive()->shouldBe(true); } function it_is_not_active() { $this->shouldNotBeActive(); // or $this->isActive()->shouldReturn(false); } }
PHPSPEC - TIPS&TRICKS
SCALAR MATCHERS
▸ $this->getBool()->shouldBeBool();
▸ $this->getObject()->shouldBeObject();
▸ $this->getString()->shouldBeString();
▸ $this->getInteger()->shouldBeInteger();
▸ $this->getDecimal()->shouldBeDecimal();
▸ $this->getCollection()->shouldBeArray();
CUSTOM MATCHERS
class Event{ private $id; private $rate; private $sum; public function getRating(): array { return [ 'id' => $this->id, 'rate' => $this->rate, 'sum' => $this->sum, ]; } } class EventSpec extends ObjectBehavior{ function it_has_rating() { $this->getRating()->shouldBeRating(); } public function getMatchers() { return [ 'beRating' => function($subject) { return array_key_exists('id', $subject) && array_key_exists('rate', $subject) && array_key_exists('sum', $subject); }, ]; } }
class Event{ public $name; public $description; public static function withNameAndDescription(string $name, string $description): Event { $instance = new self(); $instance->name = $name; $instance->description = $description; return $instance; } } class EventSpec extends ObjectBehavior{ const NAME = 'Name'; const DESCRIPTION = 'Description'; function it_has_name_and_description() { $this::withNameAndDescription(self::NAME, self::DESCRIPTION) ->shouldHaveNameAndDescription(self::NAME, self::DESCRIPTION); } public function getMatchers() { return [ 'haveNameAndDescription' => function($subject, $name, $description) { return $subject->name == $name && $subject->description == $description; }, ]; } }
WORKING WITH COLLABORATORS
http://blogophp.com
PHPSPEC - TIPS&TRICKS
CALL METHOD MULTIPLE TIMESclass SUS{ private $object; public function something() { return $this->object->rand() + $this->object->rand() + $this->object->rand(); } }
class SUSSpec extends ObjectBehavior{ function let(Object $object) { $this->beConstructedWith($object); } function it_returns_something(Object $object) { $object->rand()->willReturn(1, 2, 3); $this->something()->shouldReturn(6); } }
PHPSPEC - TIPS&TRICKS
STRONG COMPARISON ON COLLABORATOR METHODclass SUS{ private $object; public function getNumber($number) { return $this->object->something($number); } } class SUSSpec extends ObjectBehavior{ function let(Object $object) { $this->beConstructedWith($object); } function it_returns_1_for_1(Object $object) { $object->getNumber(1)->willReturn(1); $this->getNumber("1")->shouldReturn(1); } }
PHPSPEC - TIPS&TRICKS
STRONG COMPARISON ON COLLABORATOR METHODclass SUS{ private $object; public function getNumber($number) { return $this->object->something($number); } } class SUSSpec extends ObjectBehavior{ function let(Object $object) { $this->beConstructedWith($object); } function it_returns_1_for_1(Object $object) { $object->getNumber(Argument::is(1))->willReturn(1); $this->getNumber("1")->shouldReturn(1); } }
TEMPLATES
PHPSPEC - TIPS&TRICKS
SPECIFICATION TEMPLATE
▸ .phpspec/specification.tpl <?phpnamespace %namespace%;use PhpSpec\ObjectBehavior;use %subject%;/** * @mixin %subject_class% */class %name% extends ObjectBehavior{ function it_is_initializable() { $this->shouldHaveType(%subject_class%::class); }}
PHPSPEC - TIPS&TRICKS
AVAILABLE TEMPLATES
▸ class
▸ interface
▸ interface_method_signature
▸ method
▸ named_constructor_create_object
▸ named_constructor_exception
▸ private-constructor
▸ returnconstant
▸ specification
FORMATTERS
PHPSPEC - TIPS&TRICKS
FORMATTERS
bin/phpspec run —format FORMAT bin/phpspec run -fFORMAT
‣ progress (default) ‣ html ‣ pretty ‣ junit ‣ dot ‣ tap
BOSSA/PHPSPEC2-EXPECT
PHPSPEC - TIPS&TRICKS
PHPSPEC2-EXPECT?
▸ Helper that decorates any SUS with a phpspec2 lazy object wrapper
▸ https://github.com/BossaConsulting/phpspec2-expect
▸ expect(file_exists(‚crazyfile.xtn'))->toBe(true); class EventSpec extends ObjectBehavior{ function it_is_phpspec_talk() { $object = new EventOccurrence(); expect($object)->beAnInstanceOf(EventOccurrence::class); } }
VFS
<?phpnamespace AppBundle\ArticleBundle\Writer; class LocalFileSystemWriter implements WriterInterface{ /** * @var string */ protected $path; /** * @param string $path */ public function __construct($path) { $this->path = $path; } /** * @param string $content * * @return bool */ public function write($content) { $fileHandle = fopen($this->path, 'w'); $write = fwrite($fileHandle, $content); fclose($fileHandle); return $write !== false; } }
<?phpnamespace spec\AppBundle\ArticleBundle\Writer; use Vfs\FileSystem; use PhpSpec\ObjectBehavior; use AppBundle\ArticleBundle\Writer\LocalFileSystemWriter; /** * @mixin LocalFileSystemWriter */class LocalFileSystemWriterSpec extends ObjectBehavior{ function let() { $this->beConstructedWith('vfs://file.txt'); } function it_is_initializable() { $this->shouldHaveType(LocalFileSystemWriter::class); } function it_writes_a_file_to_path() { $virtualFileSystem = FileSystem::factory('vfs://'); $virtualFileSystem->mount(); $this->write('this-text-should-be-written-to-disk')->shouldReturn(true); expect(file_get_contents('vfs://file.txt'))->toBe('this-text-should-be-written-to-disk'); } }
Questions?
Thank you!