November Camp - Spec BDD with PHPSpec 2

Post on 06-May-2015

3.681 views 5 download

description

My slides on PHPSpec 2 from Symfony November Camp Stockholm. www.symfony.se/november-camp/

Transcript of November Camp - Spec BDD with PHPSpec 2

Spec BDD WITH PHPSPEC 2

November Camp Stockholm 22/11/2013flickr.com/arnolouise/3252847397/

Kacper Gunia @cakper

Software Engineer @SensioLabsUK

Symfony Certified Developer

Polish Symfony Community

Silesian PHP User Group

Software Quality

flickr.com/adforce1/2462794123/

INTERNAL Quality

flickr.com/ensh/5084228263/

EXTERNAL Quality

flickr.com/arselectronicacenter/8695704856/

INTERNAL vs

EXTERNAL

It’s NOT VS

It’s AND

InnerNAL And

EXTERNAL

flickr.com/pmiaki/6768810175/

How to ensure quality?

Test

How to test?

Automate

So you WRITE your code…

…and your tests

HOW DARE YOU?

are YOU sure Tests ARE

correct?

Test Driven Development

Red

GreenRefactor

BUT…

flickr.com/ucumari/580865728/

test Something that doesn’t

exists?

Test In TDD means

Specification

Specification describes

Behavior

BeHavior Driven Development

Naming Conventions

Tools

IMPROVED

TDD v2.0

‘TDD DONE RIGHT’

Story BDD &

Spec BDD

description of business-targeted

application behavior

STORY BDD

specification for low-level

implementation

SPEC BDD

Failing Scenario

Failing Spec

RefacTORPassing

Spec

Passing Scenario

Story BDD

SPEC BDD

Failing Scenario

Failing Spec

RefacTORPassing

Spec

Passing Scenario

External quality

INTERNAL QUALITY

BEHAT (Story BDD)

& PHPSpec

(Spec BDD)

BEHAT (Story BDD)

& PHPSpec

(Spec BDD)

FRAMEWORK SPEC BDD CREATED BY

@_MD & @EVERZET

PHPSPEC 2

Bundled With Mocking Framework - Prophecy

PHPSPEC 2

BUT…

WHY NOT PHPUNIT?

PHPUNIT Is A

TESTING TOOL

PHPSPEC Is A

DESIGN TOOL

EVIDENCE?

PHPUNIT

PHPSPEC

Differences?

tEST Suite

Specification

tEST

EXAMPLE

Assertion

expectation

class  MovieSpec  extends  ObjectBehavior  {          function  it_returns_movie_title()          {                  $this-­‐>getTitle()                            -­‐>shouldReturn('Star  Wars');          }  }

Matchers

IDENTITY MATCHER

class  MovieSpec  extends  ObjectBehavior  {      function  it_is_a_great_movie()      {          $this-­‐>getRating()-­‐>shouldBe(5);                        $this-­‐>getTitle()                    -­‐>shouldBeEqualTo('Star  Wars');                            $this-­‐>getReleaseDate()                    -­‐>shouldReturn(233366400);      }  }

COMPARISON MATCHER

class  MovieSpec  extends  ObjectBehavior  {      function  it_is_great_movie()      {          $this-­‐>getRating()-­‐>shouldBeLike('5');      }  }

THROW MATCHER

class  MovieSpec  extends  ObjectBehavior  {      function  it_does_not_allow_negative_ratings()      {          $this              -­‐>shouldThrow('\InvalidArgumentException')            -­‐>duringSetRating(-­‐3);      }  }

THROW MATCHER

class  MovieSpec  extends  ObjectBehavior  {      function  it_does_not_allow_negative_ratings()      {          $this-­‐>shouldThrow(                  new  \InvalidArgumentException(                      "Invalid  rating”                  )              )-­‐>during('setRating',  array(-­‐3));          }  }

TYPE MATCHER

class  MovieSpec  extends  ObjectBehavior  {      function  it_is_a_movie()      {          $this-­‐>shouldHaveType('Movie');          $this-­‐>shouldReturnAnInstanceOf('Movie');          $this-­‐>shouldBeAnInstanceOf('Movie');          $this-­‐>shouldImplement('Movie');      }  }

OBJECT-STATE MATCHER

class  MovieSpec  extends  ObjectBehavior  {      function  it_is_available_on_cinemas()      {          $this-­‐>shouldBeAvailableOnCinemas();      }  }  !class  Movie  {      public  function  isAvailableOnCinemas()      {  return  true;  }<?php  }

OBJECT-STATE MATCHER

class  MovieSpec  extends  ObjectBehavior  {      function  it_has_a_soundtrack()      {          $this-­‐>shouldHaveSoundtrack();      }  }  !class  Movie  {      public  function  hasSoundtrack()      {  return  true;  }  }

COUNT MATCHER

class  MovieSpec  extends  ObjectBehavior  {      function  it_has_one_director()      {          $this-­‐>getDirectors()-­‐>shouldHaveCount(1);      }  }

SCALAR MATCHER

class  MovieSpec  extends  ObjectBehavior  {      function  it_has_a_string_as_title()      {          $this-­‐>getTitle()-­‐>shouldBeString();      }  !    function  it_has_an_array_as_cast()      {          $this-­‐>getCast()-­‐>shouldBeArray();      }  }

INLINE MATCHER

class  MovieSpec  extends  ObjectBehavior  {      function  it_has_default_options()      {          $this-­‐>getOptions()-­‐>shouldHaveKey('username');      }  !    public  function  getMatchers()      {          return  [              'haveKey'  =>  function($subject,  $key)  {                  return  array_key_exists($key,  $subject);              }          ];      }  }

BUT…

Software Design is about

Messaging

TEST DOUBLES

DUMMIES

class  CinemaSpec  extends  ObjectBehavior  {      /**        *  @param  BoxOffice  $boxOffice        */      function  it_is_a_cinema($boxOffice)      {          $this-­‐>beConstructedWith($boxOffice);          $this-­‐>shouldHaveType('Cinema');      }  }

STUBS

class  CinemaSpec  extends  ObjectBehavior  {      /**        *  @param  Movie  $movie        */      function  it_displays_big_movie_title($movie)      {          $movie-­‐>getTitle()                      -­‐>willReturn('Star  Wars’);  !        $this-­‐>displayTitle($movie)                    -­‐>shouldReturn('<h1>Star  Wars</h1>');      }  }

MOCKS

class  CinemaSpec  extends  ObjectBehavior  {      /**        *  @param  DvdPlayer  $dvdPlayer        *  @param  MovieDisc  $movieDisc        */      function  it_plays_movie($dvdPlayer,  $movieDisc)      {          $dvdPlayer-­‐>playDisc($movieDisc)                              -­‐>shouldBeCalled();  !        $this-­‐>setPlayer($dvdPlayer);          $this-­‐>playMovie($movieDisc);      }  }

SPIES

class  CinemaSpec  extends  ObjectBehavior  {      /**        *  @param  DvdPlayer  $dvdPlayer        *  @param  MovieDisc  $movieDisc        */      function  it_plays_movie($dvdPlayer,  $movieDisc)      {          $this-­‐>setPlayer($dvdPlayer);          $this-­‐>playMovie($movieDisc);  !        $dvdPlayer-­‐>playDisc($movieDisc)                              -­‐>shouldHaveBeenCalled();      }  }

SET UP & TEAR DOWN

class  CinemaSpec  extends  ObjectBehavior  {          /**            *  @param  BoxOffice  $boxOffice            */          function  let($boxOffice)          {                  $this-­‐>beConstructedWith($boxOffice);          }  !        function  letGo()          {                  $this-­‐>tellPeopleToGoHome();          }  }

BUT…

HOW TO ‘DESIGN’?

1. WRITE NO PRODUCTION CODE EXCEPT TO PASS A FAILING TEST

2. WRITE ONLY ENOUGH OF A TEST TO DEMONSTRATE A FAILURE

3. WRITE ONLY ENOUGH PRODUCTION CODE TO PASS A TEST

THREE RULES OF TDD

1. Passes all the tests 2. Express every idea we need to

express 3. Contains no duplication 4. Minimized the number of classes,

methods and other moving parts

4 RULES OF SIMPLE DESIGN

1. CODE SMELLS 2. TEST SMELLS 3. DRY SMELLS 4. …and others

SMELLS

And?

QUICK START

{          "require-­‐dev":  {                  "phpspec/phpspec":  "2.0.*@dev"          },          "config":  {                  "bin-­‐dir":  "bin"          },          "autoload":  {"psr-­‐0":  {"":  "src"}}  }

http://phpspec.net/

THANK YOU!

JOIND.IN/10130

?