Upload
kacper-gunia
View
3.686
Download
5
Embed Size (px)
DESCRIPTION
My slides on PHPSpec 2 from Symfony November Camp Stockholm. www.symfony.se/november-camp/
Citation preview
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/