phpspec2: SUS and collaborators
Good evening my dear BDD practitioners. Today is the first day of the new era. Era of full stack BDD in php. Today weâve released first (and even second one, couple hours ago) alpha release of the first real SpecBDD tool for php - phpspec. Weâre really happy about this tool, what it offers and especially development style it enforces. There are lot of improvement to traditional testing tools inside, but also, plenty of things that not only different but unusual for other frameworks in the area (even SpecBDD ones).
First of all, phpspec2 is not a testing framework. Itâs a SpecBDD tool. It means, testing is not our primary aim. Toolâs job is to enforce and make SpecBDD in project flawless and phpspec2 uses all available methods to do that - state of the artformatters, class and method generators and lot of othe fancy buzzwords :) Today, i want to explain you the heart of phpspec2 - SUS, collaborators and prophets.
SUS
SUS stands for Subject Under Specification. SUS is this thing youâre currently describing in opened specification. Itâs this unexisting object, on which youâre calling unexisting methods and assuming future outcomes. Most important thing? There could be only one SUS in specification. It means, youâre always describing one object at a time. Because you hardly could concentrate even on single unexisting subject. Forcing yourself to concentrate on multiple is like suicide.
Iâve told you already, that phpspec enforces some specific development style. In this particular case, development style is one subject per specification. How phpspec2 enforces it? It just doesnât give you ability to describe expectations about other subjects. You just donât have any easy way to do nasty (in SpecBDD terms) things :) You have no choice except doing things right. And believe me, it wouldnât take long to see benefits.
So, in phpspec2 single Specification describes single subject. What is Specification? Itâs list of examples about how your object should behave! And what happens when thereâs only one subject possible? You find abilities to make things cleaner. If thereâs only one object youâre interested at a time, then you could just say this:
$this->getTitle();
Awesome, isnât it? Thatâs how you describe objects with phpspec2. Youâre pretending that youâre inside this object and describing itâs behavior and your expectations about it through matchers:
$this->getTitle()->shouldReturn(âmyselfâ);
This is possible, because in phpspec2 your SUS is wrapped into special object, called Prophet. This object can proxy method or property calls into original object and make assumptions about return values using matchers. Coolest thing about that? When thereâs no object or method implemented yet (usual stuff for BDD), instead of fatal error, youâll get clean exception message, and even more - thereâs special exception listener, which will offer to generate this class or method for you.
Also, all return values from your SUS are wrapped into this Prophet object too:
$this->getPayment()->getType()->shouldReturn(âvisaâ);
Collaborators
Ok, you can describe your object and make assumptions about itâs self-consistent behavior. But we know that world is much more complicated than that. In real world, most objects are interacting with each other. For each object in the system, thereâs at least one collaborator object, which communicates with your SUS:
public function payWith(CreditCardInterface $card) { $card->withdraw($this->amount); }
There are 2 ways to handle such situations in classical world of testing - classical and mocking approaches. In terms of TDD, itâs Classical and London TDD. First (classical) operates with real or almost-real objects. Every time you need collaborator, youâre instantiating real object for that. Mockists (LondonTDD) approach is to mock all the collaborators. This way, you can concentrate on thing that matters right now and keep actual collaborator behavior for later explanation. In TDD world, there are 2 camps of developers - classicists (those, who prefer classical approach) and mockists (those, who prefer mock everything). How it relates to phpspec2? SpecBDD is mockists methodology :) Simply because you canât avoid insanity if youâll try to describe 5 objects through classical approach without implementing those 5 objects :) SpecBDD tells you - concentrate on single object at a time. And thereâs only one way to do that - through mocks.
Again. Iâve told you already, that phpspec enforces some specific development style :) In this particular case, development style is mocking every collaborator. In phpspec2, you have examples instead of test cases. And examples could have dependencies (which are collaborators). And in php, method dependencies are defined through arguments, right?
public function it_should...($mock1) { $mock1->beAMockOf(âSome\Classâ); }
Yeah, this looks little bit unnatural. Thatâs why weâre using natural phpdoc syntax to define them usually:
/** * @param Some\Class $mock1 * @param Some\OtherClass $mock2 */ public function it_should...($mock2, $mock1) { }
Easy, right?
Ok, phpspec2 forces you to use mocks as collaborators. But it doesnât mean you need to deal with ugly mocking system youâve seen in other tools before. In case, where 50% of your development process are mocks, those mocks should be simple. But in most cases, mocks arenât simple, arenât they? Thatâs because usage of mocks usually involves couple of complex stages to be done during their usage:
// 1. expectations definition $payment = $this->getMock(âPaymentâ); $payment ->expects($this->once()) ->method(âpayWithâ) ->with($visa); // 2. execution $bank->process($payment); // 3. check expectations
You see, thereâs at least 2 stages every time you use mocks:
definition stage - youâre defining what methods should be called and how
execution stage - youâre executing those methods
checking stage - youâre checking expectations
In most modern frameworks, stage 3 is hidden, so you only have to deal with first 2. Good, but you still need to work with cumbersome syntax just to differentiate expectations definition from execution stages. Do you? In my opinion - no. In my whole developer life i havenât seen case where execution happens inside test case or example code (on the first level i mean). In test case youâre always defining expectations. Then why do you need to use this differentiation-driven syntax? In phpspec2, you donât:
$mock->getTitle()->willReturn(âphpspec2â)->shouldBeCalled();
Meet the phpspec2 mock. Yup, getTitle() method should be called without arguments and when it is, itâll return phpspec2. Ok, but in most cases you donât care whether it will be called or not. The only thing you care about is that when it does - it should return something. Itâs called stubbing. In phpspec2, stubs look like that:
$mock->getTitle()->willReturn(âphpspec2â);
Whatâs the difference? Thereâs no shouldBeCalled() method. Ok, letâs go to complext case. How to mock this:
public function prettify($string) { return sprintf($this->format, $string); }
Itâs so-called proxy-method. In phpspec2, youâll stub it like that:
$mock->prettify(ANY_ARGUMENT)->willReturnArgument();
This stub will be matched to any prettify() call with any single argument. And when itâs called, itâll return this argument. Thatâs phpspec2 collaborators.
So thatâs it for today, just go and try it! And donât forget to check demo video for it.













