Agile Zone is brought to you in partnership with:

I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 638 posts at DZone. You can read more from them at their website. View Full User Profile

PHPSpec: BDD for your classes

06.02.2011
| 11804 views |
  • submit to reddit

I dived into Behavior-Driven Development a bit of time ago by starting using Behat (the PHP equivalent of Cucumber) on katas and on a PHP enterprise application. But I never tried to apply a specification-first (in TDD, it would be test-first) approach in code instead of in a business-readable language.

Behavior-Driven Development can be seen as an evolution of Test-Driven Development, stricly in its outside-in version and with a refined language. In the case of the tool I'm reviewing today, BDD is implemented with code instead of with textual feature files (e.g. written in Gherkin) like in Behat.

PHPSpec is similar to (or a port of) RSpec, the original BDD Ruby tool. It fills the need for an approach different from the classical xUnit at the class level. This is how I see them:

The usage of BDD for both the application features and the single classes promote the automated-tests-are-the-specification mantra, and the use of examples sets instead of formal mathematical specification.

The suggested workflow, according to Marcello, is to start from user stories and implemented them with BDD outside-in, first by specifying the application behavior with Behat (business readable tests) and then descending into the details of classes with PHPSpec. This is similar to the famous double Red-Green-Refactor cycle of Acceptance Test-Driven Development.

BDD promotes different semantics for your tests specifications:

  •  specification classes start with Describe instead of ending with Test.
  • The prefix for methods is itShould instead of test.
  • Verification is accomplished through expectations and specifications, instead of with assertion methods. This leads to code that sounds like English.

Many BDD-friendly APIs were retrofitted on existing testing frameworks: for example PHPUnit has an assertThat() method that can be used with matchers to simulate English-like code.

I think that new testing frameworks don't bring us functional innovations: we can do the same things in PHPT, PHPUnit or PHPSpec: instantiate objects, call methods and perform verification on the results. What we gain here is new semantics (thinking of specifications and not of tests) and a better support for an outside-in workflow: starting from higher-level features and descend into classes by maintaining the same language. You'll never write an unneeded class again.

Maybe the advantages of the language are not perceived as much by people who speak (read and write, mostly) English as a foreign language. Code for us is already another language, and we may have speak in code for longer than in English.

Implementation

So how does PHPSpec work? You extend a base class, like in PHPUnit, and provide conventionally named methods. The methods will be run in isolation and one at the time.

You are provided on your path with a phpspec script to run the tests/specifications. Of course this script loads the needed dependencies so that you don't have to require_once() anything.

Trial run

Here's how to give PHPSpec a look.
The PEAR installation is recommended. At the time of this writing:

pear channel-discover pear.phpspec.net-1.1.0beta
pear install --alldeps phpspec/PHPSpec

You may have to upgrade your pear installation before (pear upgrade).

--alldeps is fundamental as a missing Console_Color will prevent phpspec from running. The 1.1.0-beta suffix is to avoid pear complaining that the package is not stable. I tried the 1.1.1 version but the phpspec script was missing afterwards.

Going from the installation to a running test is a matter of seconds. phpspec targets command line usage, with colors:

Of course the Api you'll use is not familiar. The project probably will need more documentation in the future, but it's normal since we are accustomed to the huge existing coverage of PHPUnit (I wrote an ebook on that.)

Mocks&Stubs

You can use Mockery or Phake, PEAR-installable independent Test Double frameworks, in order to produce, well, Test Doubles. There is also an internal implementation in a separate PEAR package, PHPSpec_Mocks; its Api is a bit ugly since it creates doubles via global functions, but it probably will integrate better due to the lack of setup code and its primitives.

Show us some code!

Here it is, my DescribeFizzBuzz.php file.

<?php
/**
 * FizzBuzz is a small kata where the goal is developing a simple function,
 * which we partially specify here. The function... well, we'll see if this
 * specification is clear enough for you to instantly understand what it
 * should do.
 * Disclaimer: both the specification and the code are incomplete, for the 
 * purpose of showing all the basic features of PHPSpec.
 */
class DescribeFizzBuzz extends \PHPSpec\Context
{
    private $fizzbuzz;

    public function before()
    {
        $this->fizzbuzz = new FizzBuzz();
    }

    public function itShouldReturnTheSameNumberForOrdinaryNumbers()
    {
        $this->spec($this->fizzbuzz->say(1))->should->equal(1);
        $result = $this->spec($this->fizzbuzz->say(2));
        $result->should->be(2);
    }

    public function itShouldNotReturnTheSameNumberForMultiplesOf3()
    {
        $result = $this->spec($this->fizzbuzz->say(3));
        $result->shouldNot->be(3);
        $result->should->be('Fizz');
    }

    public function itShouldBeAnObject()
    {
        $this->spec($this->fizzbuzz)->should->beAnInstanceOf('FizzBuzz');
    }

    public function itShouldNotReturnTheSameNumberForMultiplesOf5()
    {
        $this->pending('Buzz implementation needed.');
    }

    public function itShouldReturnBangForMultiplesOf7()
    {
        $this->fail('This scenario should already be specified.');
    }
}

class FizzBuzz
{
    public function say($number)
    {
        if ($number % 3 == 0) {
            return 'Fizz';
        }
        return $number;
    }
}

If you want more, checkout Marcello's presentation.

Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Gregg Flynn replied on Thu, 2011/06/02 - 1:15pm

I think your graphic should be PHPSpec at the top, instead of PHPUnit.

Giorgio Sironi replied on Sun, 2011/06/05 - 7:24am in response to: Gregg Flynn

I don't think PHPSpec is appropriate at the application level; it was presented to me by one of its developers for usage on the class scale, while Behat and similar tools can take care of the specification of application features.

koen replied on Mon, 2011/06/06 - 6:10am

Do you know any testing framework in PHP that let me do the given/when/then in that order when using mocks? In PHPUnit you have to specify the expectations on a mock first while this is often the result I want to check against (the 'then' part) when designing mock driven.

 Also, from the example it doesn't look like PHPSpec would be that interesting compared with regular PHPUnit tests. I don't find it more readable or more helpful towards design. In the past I have used Should in the method names for PHPUnit tests and that seemed to suffice to get into a more behaviour driven mindset.

Gregg Flynn replied on Mon, 2011/06/06 - 11:37am

I'm sorry, maybe I misunderstand. Isn't PHPSpec a BDD tool? In my understanding, one of the goals of BDD is to raise the test perspective to a higher level than units. Unit testing at the class level, and BDD at the application level.

Giorgio Sironi replied on Sat, 2011/06/11 - 5:27am in response to: Gregg Flynn

Yes, but for higher-level test a tool like Behat, which reads feature files instead of PHP code is usually preferred. The logic is that non-programmers can read and edit those feature files, while they can't read PHPSpec-based code.

Carla Brian replied on Fri, 2012/05/04 - 8:09pm

I think this is really an effective application for PhP. I have heard good feedbacks on this. I will try this one. - Arhtur van der Vant

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.