5.3. The Spec Domain Specific Language (DSL)

PHPSpec writes coded Examples of behaviour using a Domain Specific Language (DSL) for describing expectations. The DSL was designed to approximate plain grammatically accurate English so it is intuitive to use, read and comprehend.

The basic form of the DSL is to attach an Expectation (should or should not) and a Matcher (be, beAnInstanceOf, equal, etc.) to the value or object passed to a new Spec. This approach leads to a relatively easy to read sentance requiring minimal translation into the plain English (or other language!) we normally think in. Since the translation effort is minimised, and is closer to how we really think, it's invariably easy to review, critique and modify.

Example 5.5. Example Spec DSL: Bowling should not be an instance of Logger

$bowling = new Bowling;
$this->spec($bowling)->shouldNot->beAnInstanceOf('Logger');

5.3.1. The Actual Value Term

In a PHPSpec Example method block, the DSL is instantiated using a call to the PHPSpec_Context::spec(). This accepts three possible parameter groupings.

  1. A scalar value, i.e. string, integer, boolean, float, or array

  2. An object

  3. An object name, together with any constructor parameters

Example 5.6. Actual Term: Scalar Examples

$this->spec('i am a string')->should-beString();
$this->spec(567)->should->equal(567);
$this->spec(array(1, 2, 3))->shouldNot->beEmpty();

Example 5.7. Actual Term: Object Examples

$this->spec(new Bowling)->should->beAnInstanceOf('Bowling');

$bowling = new Bowling;
$this->spec($bowling)->shouldNot->havePlayers();

Example 5.8. Actual Term: Object Name With Constructor Params

$this->spec('Bowling', new Player('Joe'), new Player('Jim'))->should->havePlayers();

5.3.2. The Expectation Term (Should or Should Not)

Just as with English, all expectations fall into one of two possible classes. Those you expect to fail, and those you expect to pass. Whether you wish a Matched Actual Value or an Unmatched Actual Value to be interpreted as a pass depends on the use of the DSL should or shouldNot phrases.

All the examples below are expected to pass.

Example 5.9. Expectation Term: Various Passing Examples

$spec->( array() )->should->beEmpty();
$spec->('Bowling')->shouldNot->havePlayers();
$spec->('i am a string')->should->match("/^[a-z ]$/");
$spec->(is_int('string'))->shouldNot->beTrue();

5.3.3. The Matcher Term

Whereas Unit Testing frameworks rely on assertions, PHPSpec splits the responsibility between an Expectation Term and a Matcher. A Matcher is a simple object which compares an Actual Value Term with the expected value passed to the Matcher method in the DSL for a positive or negative match. The form of a Matcher is ruled by the PHPSpec_Matcher_Interface interface so you can write custom Matchers (pending feature).

An already expansive range of Matchers are provided by the PHPSpec framework. [Note: Some are still awaiting development.]

A Matcher is general appended as the last term to a Spec as demonstrated in earlier examples.

5.3.3.1. Matchers Included In PHPSpec

Note that all Matchers will return a boolean when called thus ending the fluent interface of the Spec. Parameters marked NULL generally mean a parameter is not required (the expected value is implicit in the Matcher name).

Table 5.1. PHPSpec Matchers

Matcher Method Explanation

bool be (mixed $expected)

Identical to using equal() and reflects general English usage.
bool beEqualTo (mixed $expected) Identical to using equal() and reflects general English usage.
bool equal (mixed $expected) Attempts to match the expected value on an equal basis intelligently comparing scalar values, object class, array content, or other metrics generally associated with two items being equivelant.
bool beTrue (null $expected) Matches the actual value against TRUE.
bool beFalse (null $expected) Matches the actual value against FALSE.
bool beNull (null $expected) Checks if the actual value is NULL.
bool beEmpty (mixed $expected) Checks if the actual value is empty (using empty()).

bool beSet (null $expected)

Checks if the actual value is set (using isset()).

bool beAnInstanceOf (string $expected)

Determines if the actual value is both an object and an instance of the class type provided.

bool beOfType (string $expected)

Checks the value type accepting a string decription of the expected type (e.g. 'int', 'stdClass').

bool beInt (null $expected)

Checks if the actual value is an integer. This is a precise check - the string form of an integer will not match.

bool beArray (null $expected)

Checks if the actual value is an array.

bool beString (null $expected)

Checks if the actual value is a string.

bool beFloat (null $expected)

Checks if the actual value is a float.

bool beObject (null $expected)

Checks if the actual value is an object; does not perform type comparison on class type.

bool beGreaterThan (mixed $expected)

Checks if the actual value is greater than (>) the expected value provided.

bool beLessThan (mixed $expected)

Checks if the actual value is less than (<) the expected value provided

bool beGreaterThanOrEqualTo (mixed $expected)

Checks if the actual value is greater than or equal to (>=) the expected value provided

bool beLessThanOrEqualTo (mixed $expected)

Checks if the actual value is less than or equal to (<=) the expected value provided

5.3.3.2. Predicate Matchers

A Predicate Matcher is a Matcher which captures it's actual value from an object being specified. It does so by seeking and then calling a method of the form isSomething() or hasSomething(). We saw this already in previous DSL examples where the DSL method havePlayers() is translated into a call to Bowling::hasPlayers(). A boolean result from the called method is then compared to a boolean TRUE to check for a positive or negative match.

Example 5.10. Example of Classes and Predicate Matcher Calls

class Insect {

    public function isInsect() {
        return true;
    }

    public function hasWings() {
        return true;
    }

}

class Flea extends Insect {

    public function hasWings() {
        return false; // Fleas are wingless blood sucking things
    }

}

class DescribeFlea extends PHPSpec_Context {

    public function itShouldBeAnInsect()
    {
        $flee = new Flea;
        $this->spec($flea)->should->beAnInsect(); // Flea::isInsect() == TRUE
    }

    public function itShouldHaveNoWings()
    {
        $flee = new Flea;
        $this->spec($flea)->shouldNot->haveWings(); // Flea::hasWings() == FALSE
    }
}

Predicate Matcher methods in the DSL allow for the use of be(), beA(), beAn() variations which are primarily for allowing grammatically correct structures and are otherwise identical. Same applies to have(), haveA(), and haveAn(). The same variations are also searched for when matching to an object's methods (even object methods can be grammatically correct!). This form of matching will eventually be expanded to allow for other predicate style calling methods. If you have any suggestions be sure to let us know.