137
PHPSpec the only Design Tool you need flickr.com/mobilestreetlife/4179063482/

PHPSpec - the only Design Tool you need - 4Developers

Embed Size (px)

DESCRIPTION

Slides from my talk at 4Developers conference in Warsaw

Citation preview

Page 1: PHPSpec - the only Design Tool you need - 4Developers

PHPSpecthe only Design Tool you need

flickr.com/mobilestreetlife/4179063482/

Page 2: PHPSpec - the only Design Tool you need - 4Developers

Kacper Gunia @cakper Software Engineer @SensioLabsUK

Symfony Certified Developer

PHPers Silesia @PHPersPL

Page 3: PHPSpec - the only Design Tool you need - 4Developers

!

‘Is my code well designed?’

Page 4: PHPSpec - the only Design Tool you need - 4Developers

’That’s not the way

I would have done it…’

Page 5: PHPSpec - the only Design Tool you need - 4Developers

It’s hard tochange!

Page 6: PHPSpec - the only Design Tool you need - 4Developers

We're afraid to change it…

Page 7: PHPSpec - the only Design Tool you need - 4Developers

We cannotreuse it!

Page 8: PHPSpec - the only Design Tool you need - 4Developers

So what

Design is about?

Page 9: PHPSpec - the only Design Tool you need - 4Developers

‘The key in making great and growable systems is much more to design how its

modules communicate rather than what their internal

properties and behaviors should be.’ Alan Kay

Page 10: PHPSpec - the only Design Tool you need - 4Developers

Design is about

Messaging

Page 11: PHPSpec - the only Design Tool you need - 4Developers

$orders  =  $orderRepository      -­‐>getEntityManager()        -­‐>createOrderQuery($customer)        -­‐>execute();

Page 12: PHPSpec - the only Design Tool you need - 4Developers

$orders  =  $orderRepository      -­‐>findBy($customer);  !

Page 13: PHPSpec - the only Design Tool you need - 4Developers

We have

to refactor! :)

Page 14: PHPSpec - the only Design Tool you need - 4Developers

We need two weeks to refactor! :)

Page 15: PHPSpec - the only Design Tool you need - 4Developers

We need two sprints to refactor! :|

Page 16: PHPSpec - the only Design Tool you need - 4Developers

We need two months

to refactor! :/

Page 17: PHPSpec - the only Design Tool you need - 4Developers

Refactoringis the process of restructuring existing code without changing its external behavior

Page 18: PHPSpec - the only Design Tool you need - 4Developers

4 Rules of Simple Design1. Passes its tests 2. Minimizes duplication 3. Maximizes clarity 4. Has fewer elements

Page 19: PHPSpec - the only Design Tool you need - 4Developers

…so we need to write

Tests!

Page 20: PHPSpec - the only Design Tool you need - 4Developers

how to write

Tests?

Page 21: PHPSpec - the only Design Tool you need - 4Developers

Tests Driven Development

Page 22: PHPSpec - the only Design Tool you need - 4Developers

Red

GreenRefactor

Page 23: PHPSpec - the only Design Tool you need - 4Developers

Red

GreenRefactor

Page 24: PHPSpec - the only Design Tool you need - 4Developers

But!

Page 25: PHPSpec - the only Design Tool you need - 4Developers

How to test something that doesn’t exist?

flickr.com/ucumari/580865728/

Page 26: PHPSpec - the only Design Tool you need - 4Developers

Test in TDD means

specification

Page 27: PHPSpec - the only Design Tool you need - 4Developers

Specification describes behavior

Page 28: PHPSpec - the only Design Tool you need - 4Developers

Behavior Driven Development

Page 29: PHPSpec - the only Design Tool you need - 4Developers

BDD improvesNaming Conventions Tools

Page 30: PHPSpec - the only Design Tool you need - 4Developers

Story BDD vs

Spec BDD

Page 31: PHPSpec - the only Design Tool you need - 4Developers

Story BDDdescription of business-targeted application behavior

Page 32: PHPSpec - the only Design Tool you need - 4Developers

Spec BDDspecification for low-level implementation

Page 33: PHPSpec - the only Design Tool you need - 4Developers

http://phpspec.net/

Page 34: PHPSpec - the only Design Tool you need - 4Developers

Spec BDD tool created by

@_md & @everzet

Page 35: PHPSpec - the only Design Tool you need - 4Developers

Bundled with mocking library

Prophecy

Page 36: PHPSpec - the only Design Tool you need - 4Developers

composer  create-­‐project                                      cakper/phpspec-­‐standard                    project-­‐name

Page 37: PHPSpec - the only Design Tool you need - 4Developers

But!

Page 38: PHPSpec - the only Design Tool you need - 4Developers

Isn’t it a tool just like PHPUnit?

Page 39: PHPSpec - the only Design Tool you need - 4Developers

PHPUnit is a

Testing Tool

Page 40: PHPSpec - the only Design Tool you need - 4Developers

PHPSpec is the

Design Tool

Page 41: PHPSpec - the only Design Tool you need - 4Developers
Page 42: PHPSpec - the only Design Tool you need - 4Developers

class  CustomerRepositoryTest  extends  \PHPUnit_Framework_TestCase  {          function  testClassExists()          {                  $customerRepository  =  new  \CustomerRepository;  !

               $customer  =  $customerRepository-­‐>findById(5);                  $this-­‐>assertInstanceOf('\Customer',  $customer);          }  }

Page 43: PHPSpec - the only Design Tool you need - 4Developers
Page 44: PHPSpec - the only Design Tool you need - 4Developers
Page 45: PHPSpec - the only Design Tool you need - 4Developers
Page 46: PHPSpec - the only Design Tool you need - 4Developers

class  CustomerRepositorySpec  extends  ObjectBehavior  {          function  it_is_initializable()          {                  $this-­‐>shouldHaveType('CustomerRepository');          }  }

Page 47: PHPSpec - the only Design Tool you need - 4Developers

Naming

Page 48: PHPSpec - the only Design Tool you need - 4Developers

TestCase !

Specification

Page 49: PHPSpec - the only Design Tool you need - 4Developers

Test !

Example

Page 50: PHPSpec - the only Design Tool you need - 4Developers

Assertion !

Expectation

Page 51: PHPSpec - the only Design Tool you need - 4Developers

OK, so how to specify a method?

Page 52: PHPSpec - the only Design Tool you need - 4Developers

What method can do?return a value modify state delegate throw an exception

Page 53: PHPSpec - the only Design Tool you need - 4Developers

Command-Query Separation

Page 54: PHPSpec - the only Design Tool you need - 4Developers

Commandchange the state of a system but do not return a value

Page 55: PHPSpec - the only Design Tool you need - 4Developers

Queryreturn a result and do not change the state of the system (free of side effects)

Page 56: PHPSpec - the only Design Tool you need - 4Developers

Never both!

Page 57: PHPSpec - the only Design Tool you need - 4Developers
Page 58: PHPSpec - the only Design Tool you need - 4Developers
Page 59: PHPSpec - the only Design Tool you need - 4Developers
Page 60: PHPSpec - the only Design Tool you need - 4Developers
Page 61: PHPSpec - the only Design Tool you need - 4Developers
Page 62: PHPSpec - the only Design Tool you need - 4Developers
Page 63: PHPSpec - the only Design Tool you need - 4Developers

class  CustomerRepositorySpec  extends  ObjectBehavior  {          function  it_loads_user_preferences()          {                  $customer  =  $this-­‐>findById(5);  !

               $customer-­‐>shouldBeAnInstanceOf('\Customer');          }  }

Page 64: PHPSpec - the only Design Tool you need - 4Developers

Matchers

Page 65: PHPSpec - the only Design Tool you need - 4Developers

TypeshouldBeAnInstanceOf(*) shouldReturnAnInstanceOf(*) shouldHaveType(*)

Page 66: PHPSpec - the only Design Tool you need - 4Developers

$customer-­‐>shouldBeAnInstanceOf('\Customer');

Page 67: PHPSpec - the only Design Tool you need - 4Developers

Identity ===shouldReturn(*) shouldBe(*) shouldEqual(*) shouldBeEqualTo(*)

Page 68: PHPSpec - the only Design Tool you need - 4Developers

$this-­‐>findById(-­‐1)-­‐>shouldReturn(null);

Page 69: PHPSpec - the only Design Tool you need - 4Developers

Comparison ==shouldBeLike(*)

Page 70: PHPSpec - the only Design Tool you need - 4Developers

$this-­‐>getAmount()-­‐>shouldBeLike(5);

Page 71: PHPSpec - the only Design Tool you need - 4Developers

Throwthrow(*)->during*()

Page 72: PHPSpec - the only Design Tool you need - 4Developers

$this-­‐>shouldThrow(‘\InvalidArgumentException’)          -­‐>duringFindByCustomer(null);

Page 73: PHPSpec - the only Design Tool you need - 4Developers

Object StateshouldHave*()

Page 74: PHPSpec - the only Design Tool you need - 4Developers

$car-­‐>hasEngine();  !

$this-­‐>shouldHaveEngine();

Page 75: PHPSpec - the only Design Tool you need - 4Developers

ScalarshouldBeString() shouldBeArray()

Page 76: PHPSpec - the only Design Tool you need - 4Developers

CountshouldHaveCount(*)

Page 77: PHPSpec - the only Design Tool you need - 4Developers

Or write your own

Inline Matcher

Page 78: PHPSpec - the only Design Tool you need - 4Developers

function  it_should_have_poland_as_avialable_country()  {          $this-­‐>getCountryCodes()-­‐>shouldHaveValue('PL');  }  !

public  function  getMatchers()  {          return  [                  'haveValue'  =>  function  ($subject,  $value)  {                                  return  in_array($value,  $subject);                          }          ];  }

Page 79: PHPSpec - the only Design Tool you need - 4Developers

But!

Page 80: PHPSpec - the only Design Tool you need - 4Developers

Design is about

Messaging!

Page 81: PHPSpec - the only Design Tool you need - 4Developers

And (so far) there is no messaging…

Page 82: PHPSpec - the only Design Tool you need - 4Developers

London School

Page 83: PHPSpec - the only Design Tool you need - 4Developers

Mockist TDDonly tested object is real

Page 84: PHPSpec - the only Design Tool you need - 4Developers

Test Doubles

Page 85: PHPSpec - the only Design Tool you need - 4Developers

Dummytested code requires parameter but doesn’t need to use it

Page 86: PHPSpec - the only Design Tool you need - 4Developers

function  let(EntityManager  $entityManager)  {          $this-­‐>beConstructedWith($entityManager);  }  !

function  it_returns_customer_by_id()  {          $customer  =  $this-­‐>findById(5);  !

       $customer-­‐>shouldBeAnInstanceOf('\Customer');          }  }

Page 87: PHPSpec - the only Design Tool you need - 4Developers

function  let(EntityManager  $entityManager)  {          $this-­‐>beConstructedWith($entityManager);  }  !

function  it_returns_customer_by_id()  {          $customer  =  $this-­‐>findById(5);  !

       $customer-­‐>shouldBeAnInstanceOf('\Customer');          }  }

Page 88: PHPSpec - the only Design Tool you need - 4Developers

Stubprovides "indirect input" to the tested code

Page 89: PHPSpec - the only Design Tool you need - 4Developers

function  it_bolds_the_output(Stream  $stream)  {          $stream-­‐>getOutput()                        -­‐>willReturn('4  Developers');  !

       $this-­‐>bold($stream)                    -­‐>shouldReturn('<b>4  Developers</b>’);  }

Page 90: PHPSpec - the only Design Tool you need - 4Developers

function  it_bolds_the_output(Stream  $stream)  {          $stream-­‐>getOutput()                        -­‐>willReturn('4  Developers');  !

       $this-­‐>bold($stream)                    -­‐>shouldReturn('<b>4  Developers</b>’);  }

Page 91: PHPSpec - the only Design Tool you need - 4Developers

Mocksverifies "indirect output” of the tested code

Page 92: PHPSpec - the only Design Tool you need - 4Developers

function  let(Logger  $logger)  {          $this-­‐>beConstructedWith($logger);  }  !

function  it_returns_customer_by_id(Logger  $logger)  {          $logger-­‐>debug('DB  queried')                        -­‐>shouldBeCalled();  !

       $this-­‐>findById(5);  }

Page 93: PHPSpec - the only Design Tool you need - 4Developers

function  let(Logger  $logger)  {          $this-­‐>beConstructedWith($logger);  }  !

function  it_returns_customer_by_id(Logger  $logger)  {          $logger-­‐>debug('DB  queried')                        -­‐>shouldBeCalled();  !

       $this-­‐>findById(5);  }

Page 94: PHPSpec - the only Design Tool you need - 4Developers

Spyverifies "indirect output” by asserting the expectations afterwards

Page 95: PHPSpec - the only Design Tool you need - 4Developers

function  let(Logger  $logger)  {          $this-­‐>beConstructedWith($logger);  }  !

function  it_returns_customer_by_id(Logger  $logger)  {          $this-­‐>findById(5);  !

       $logger-­‐>debug('DB  queried')                        -­‐>shouldHaveBeenCalled();  }

Page 96: PHPSpec - the only Design Tool you need - 4Developers

function  let(Logger  $logger)  {          $this-­‐>beConstructedWith($logger);  }  !

function  it_returns_customer_by_id(Logger  $logger)  {          $this-­‐>findById(5);  !

       $logger-­‐>debug('DB  queried')                        -­‐>shouldHaveBeenCalled();  }

Page 97: PHPSpec - the only Design Tool you need - 4Developers

(a bit more) complex example

Page 98: PHPSpec - the only Design Tool you need - 4Developers

function  let(SecurityContext  $securityContext)  {          $this-­‐>beConstructedWith($securityContext);    }  !

function  it_loads_user_preferences(          GetResponseEvent  $event,  SecurityContext  $securityContext,            TokenInterface  $token,  User  $user)    {          $securityContext-­‐>getToken()-­‐>willReturn($token);          $token-­‐>getUser()-­‐>willReturn($user);  !

       $user-­‐>setPreferences(Argument::type('Preferences'))                    -­‐>shouldBeCalled();  !

       $this-­‐>handle($event);    }

Page 99: PHPSpec - the only Design Tool you need - 4Developers

function  let(SecurityContext  $securityContext)  {          $this-­‐>beConstructedWith($securityContext);    }  !

function  it_loads_user_preferences(          GetResponseEvent  $event,  SecurityContext  $securityContext,            TokenInterface  $token,  User  $user)    {          $securityContext-­‐>getToken()-­‐>willReturn($token);          $token-­‐>getUser()-­‐>willReturn($user);  !

       $user-­‐>setPreferences(Argument::type('Preferences'))                    -­‐>shouldBeCalled();  !

       $this-­‐>handle($event);    }

Page 100: PHPSpec - the only Design Tool you need - 4Developers

function  let(SecurityContext  $securityContext)  {          $this-­‐>beConstructedWith($securityContext);    }  !

function  it_loads_user_preferences(          GetResponseEvent  $event,  SecurityContext  $securityContext,            TokenInterface  $token,  User  $user)    {          $securityContext-­‐>getToken()-­‐>willReturn($token);          $token-­‐>getUser()-­‐>willReturn($user);  !

       $user-­‐>setPreferences(Argument::type('Preferences'))                    -­‐>shouldBeCalled();  !

       $this-­‐>handle($event);    }

Page 101: PHPSpec - the only Design Tool you need - 4Developers

function  let(SecurityContext  $securityContext)  {          $this-­‐>beConstructedWith($securityContext);    }  !

function  it_loads_user_preferences(          GetResponseEvent  $event,  SecurityContext  $securityContext,            TokenInterface  $token,  User  $user)    {          $securityContext-­‐>getToken()-­‐>willReturn($token);          $token-­‐>getUser()-­‐>willReturn($user);  !

       $user-­‐>setPreferences(Argument::type('Preferences'))                    -­‐>shouldBeCalled();  !

       $this-­‐>handle($event);    }

Page 102: PHPSpec - the only Design Tool you need - 4Developers

function  let(SecurityContext  $securityContext)  {          $this-­‐>beConstructedWith($securityContext);    }  !

function  it_loads_user_preferences(          GetResponseEvent  $event,  SecurityContext  $securityContext,            TokenInterface  $token,  User  $user)    {          $securityContext-­‐>getToken()-­‐>willReturn($token);          $token-­‐>getUser()-­‐>willReturn($user);  !

       $user-­‐>setPreferences(Argument::type('Preferences'))                    -­‐>shouldBeCalled();  !

       $this-­‐>handle($event);    }

Page 103: PHPSpec - the only Design Tool you need - 4Developers

But mocking becomes painful…

Page 104: PHPSpec - the only Design Tool you need - 4Developers

And it smells…

Page 105: PHPSpec - the only Design Tool you need - 4Developers

Law of Demeterunit should only talk to its friends; don't talk to strangers

Page 106: PHPSpec - the only Design Tool you need - 4Developers

It’s time to refactor! :)

Page 107: PHPSpec - the only Design Tool you need - 4Developers

function  it_loads_user_preferences(          GetResponseEvent  $event,            SecurityContext  $securityContext,            TokenInterface  $token,  User  $user)    {          $securityContext-­‐>getToken()-­‐>willReturn($token);          $token-­‐>getUser()-­‐>willReturn($user);  !

       $user-­‐>setPreferences(Argument::type('Preferences'))                    -­‐>shouldBeCalled();  !

       $this-­‐>handle($event);    }

Page 108: PHPSpec - the only Design Tool you need - 4Developers

function  it_returns_user_from_token(          SecurityContext  $securityContext,            TokenInterface  $token,  User  $user)  {          $securityContext-­‐>getToken()-­‐>willReturn($token);          $token-­‐>getUser()-­‐>willReturn($user);  !

       $this-­‐>getUser()-­‐>shouldRetun($user);  }  !

Page 109: PHPSpec - the only Design Tool you need - 4Developers

public  function  __construct(SecurityContext  $securityContext){          $this-­‐>securityContext  =  $securityContext;  }  !

public  function  getUser()  {          $token  =  $this-­‐>securityContext-­‐>getToken();          if  ($token  instanceof  TokenInterface)  {                  return  $token-­‐>getUser();          }  !

       return  null;  }

Page 110: PHPSpec - the only Design Tool you need - 4Developers

function  it_loads_user_preferences(          GetResponseEvent  $event,            SecurityContext  $securityContext,            TokenInterface  $token,  User  $user)    {          $securityContext-­‐>getToken()-­‐>willReturn($token);          $token-­‐>getUser()-­‐>willReturn($user);  !

       $user-­‐>setPreferences(Argument::type('Preferences'))                    -­‐>shouldBeCalled();  !

       $this-­‐>handle($event);    }

Page 111: PHPSpec - the only Design Tool you need - 4Developers

function  it_loads_user_preferences(          GetResponseEvent  $event,                                  DomainSecurityContext  $securityContext,          User  $user)  {          $securityContext-­‐>getUser()-­‐>willReturn($user);  !

       $user-­‐>setPreferences(Argument::type(‘Preferences'))                    -­‐>shouldBeCalled();  !

       $this-­‐>handle($event);  }  

Page 112: PHPSpec - the only Design Tool you need - 4Developers

Composition over Inheritance

separation of concerns small, well focused objects composition is simpler to test

Page 113: PHPSpec - the only Design Tool you need - 4Developers

public  function  __construct(SecurityContext  $securityContext){          $this-­‐>securityContext  =  $securityContext;  }  !

public  function  getUser()  {          $token  =  $this-­‐>securityContext-­‐>getToken();          if  ($token  instanceof  TokenInterface)  {                  return  $token-­‐>getUser();          }  !

       return  null;  }

Page 114: PHPSpec - the only Design Tool you need - 4Developers

We can still improve

Page 115: PHPSpec - the only Design Tool you need - 4Developers

function  it_loads_user_preferences(          GetResponseEvent  $event,                                  DomainSecurityContext  $securityContext,          User  $user)  {          $securityContext-­‐>getUser()-­‐>willReturn($user);  !

       $user-­‐>setPreferences(Argument::type(‘Preferences'))                    -­‐>shouldBeCalled();  !

       $this-­‐>handle($event);  }  

Page 116: PHPSpec - the only Design Tool you need - 4Developers

function  it_loads_user_preferences(          GetResponseEvent  $event,                                  DomainSecurityContextInterface  $securityContext,          User  $user)  {          $securityContext-­‐>getUser()-­‐>willReturn($user);  !

       $user-­‐>setPreferences(Argument::type(‘Preferences'))                    -­‐>shouldBeCalled();  !

       $this-­‐>handle($event);  }  

Page 117: PHPSpec - the only Design Tool you need - 4Developers

Dependency Inversion Principle

Page 118: PHPSpec - the only Design Tool you need - 4Developers

high-level modules should not depend on low-level modules; both should depend on abstractions

DIP states:

Page 119: PHPSpec - the only Design Tool you need - 4Developers

DIP states:abstractions should not depend upon details; details should depend upon abstractions

Page 120: PHPSpec - the only Design Tool you need - 4Developers
Page 121: PHPSpec - the only Design Tool you need - 4Developers
Page 122: PHPSpec - the only Design Tool you need - 4Developers

Isn’t it overhead?

Page 123: PHPSpec - the only Design Tool you need - 4Developers
Page 124: PHPSpec - the only Design Tool you need - 4Developers

What are benefits of using PHPSpec?

Page 125: PHPSpec - the only Design Tool you need - 4Developers

TDD-cycle oriented tool

Page 126: PHPSpec - the only Design Tool you need - 4Developers

ease Mocking

Page 127: PHPSpec - the only Design Tool you need - 4Developers

focused on Messaging

Page 128: PHPSpec - the only Design Tool you need - 4Developers

encourage injecting right Collaborators

Page 129: PHPSpec - the only Design Tool you need - 4Developers

and following Demeter Low

Page 130: PHPSpec - the only Design Tool you need - 4Developers

enables Refactoring

Page 131: PHPSpec - the only Design Tool you need - 4Developers

and gives you Regression Safety

Page 132: PHPSpec - the only Design Tool you need - 4Developers

and it’s trendy ;)

Page 133: PHPSpec - the only Design Tool you need - 4Developers

Is PHPSpec the only design tool

we need?

Page 134: PHPSpec - the only Design Tool you need - 4Developers

s

Page 135: PHPSpec - the only Design Tool you need - 4Developers
Page 136: PHPSpec - the only Design Tool you need - 4Developers

So it helps ;)

Page 137: PHPSpec - the only Design Tool you need - 4Developers

Kacper Gunia Software Engineer

Symfony Certified Developer

PHPers Silesia

Thanks!