2. I like PhpSpec best because
● Talk to it like a human
● Encourages better code
● Can still use PHPUnit
○ Unit testing existing code
○ Functional / UI / integration tests
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 2
6. We write code that isn't needed
Writing wasteful code leads to:
● Bugs
● Lower productivity
● Difficulty in adding new features
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 6
7. TDD solves this
● TDD stands for "Tspecification Driven
Design"
● Work out what code would be useful,
before writing it
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 7
8. How we do TDD
Example of how your code will work
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 8
Red
9. How we do TDD
Minimum code to make it work
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 9
Red Green
10. How we do TDD
Make code simpler and clearer,
without changing behaviour
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 10
Red Green Refactor
11. How we do TDD
Start again
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 11
Red Green Refactor
23. Spec is generated automatically
$ cat spec/VendingMachineSpec.php
<?php
class VendingMachineSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('VendingMachine');
}
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 23
24. Run the specification
$ vendor/bin/phpspec run
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 24
25. Run the specification
$ vendor/bin/phpspec run
...
class VendingMachine does not exist.
1 specs
1 example (1 broken)
Do you want me to create `VendingMachine` for you?
[Y/n]
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 25
26. Run the specification
$ vendor/bin/phpspec run
...
class VendingMachine does not exist.
1 specs
1 example (1 broken)
Do you want me to create `VendingMachine` for you?
[Y/n]
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 26
27. Get PhpSpec to fix stuff for you
...
Do you want me to create `VendingMachine` for you?
[Y/n]
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 27
28. Get PhpSpec to fix stuff for you
...
Do you want me to create `VendingMachine` for you?
[Y/n]
y
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 28
29. Get PhpSpec to fix stuff for you
Do you want me to create `VendingMachine` for you?
[Y/n]
y
Class VendingMachine created in
/home/dave/demo/src/VendingMachine.php.
1 specs
1 example (1 passed)
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 29
30. Get PhpSpec to fix stuff for you
Do you want me to create `VendingMachine` for you?
[Y/n]
y
Class VendingMachine created in
/home/dave/demo/src/VendingMachine.php.
1 specs
1 example (1 passed)
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 30
37. How we do TDD
Example of how your code will work
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 37
Red
38. Example of how your code will work
Example: it makes funds available
When I call
$this->insertCoin(20);
then
$this->availableFunds() should be 20
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 38
39. Example of how your code will work
Example: it makes funds available
When I call
$this->insertCoin(20);
then
$this->availableFunds() should be 20
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 39
40. Example of how your code will work
Example: it makes funds available
When I call
$this->insertCoin(20);
then
$this->availableFunds() should be 20
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 40
41. Example of how your code will work
Example: it makes funds available
When I call
$this->insertCoin(20);
then
$this->availableFunds() should be 20
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 41
42. Example of how your code will work
function it_makes_funds_available()
When I call
$this->insertCoin(20);
then
$this->availableFunds() should be 20
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 42
43. Example of how your code will work
function it_makes_funds_available()
When I call
$this->insertCoin(20);
then
$this->availableFunds() should be 20
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 43
44. Example of how your code will work
function it_makes_funds_available()
{
$this->insertCoin(20);
$this->availableFunds() should be 20
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 44
45. Example of how your code will work
function it_makes_funds_available()
{
$this->insertCoin(20);
$this->availableFunds() should be 20
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 45
46. Example of how your code will work
function it_makes_funds_available()
{
$this->insertCoin(20);
$this->availableFunds()->shouldBe(20);
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 46
47. Example of how your code will work
function it_makes_funds_available()
{
$this->insertCoin(20);
$this->availableFunds()->shouldBe(20);
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 47
48. Example of how your code will work
class VendingmachineSpec extends ObjectBehavior
{
...
function it_makes_funds_available()
{
$this->insertCoin(20);
$this->availableFunds()->shouldBe(20);
}
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 48
50. Run the specification
$ vendor/bin/phpspec run
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 50
51. Run the specification
$ vendor/bin/phpspec run
Method VendingMachine::insertCoin not found.
Do you want me to create `VendingMachine::insertCoin()
` for you?
[Y/n]
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 51
52. Run the specification
$ vendor/bin/phpspec run
Method VendingMachine::insertCoin not found.
Do you want me to create `VendingMachine::insertCoin()
` for you?
[Y/n]
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 52
53. Get PhpSpec to fix stuff for you
n
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 53
54. Get PhpSpec to fix stuff for you
y
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 54
55. Get PhpSpec to fix stuff for you
y
Method VendingMachine::insertCoin() has been created.
Method VendingMachine::availableFunds not found.
Do you want me to create `VendingMachine::
availableFunds()` for you?
[Y/n]
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 55
56. Get PhpSpec to fix stuff for you
y
Method VendingMachine::insertCoin() has been created.
Method VendingMachine::availableFunds not found.
Do you want me to create `VendingMachine::
availableFunds()` for you?
[Y/n]
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 56
57. Get PhpSpec to fix stuff for you
y
Method VendingMachine::insertCoin() has been created.
Method VendingMachine::availableFunds not found.
Do you want me to create `VendingMachine::
availableFunds()` for you?
[Y/n]
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 57
58. Get PhpSpec to fix stuff for you
y
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 58
59. Get PhpSpec to fix stuff for you
y
Method VendingMachine::availableFunds() has been
created.
expected [integer:20], but got null.
1 specs
2 examples (1 passed, 1 failed)
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 59
60. Get PhpSpec to fix stuff for you
y
Method VendingMachine::availableFunds() has been
created.
expected [integer:20], but got null.
1 specs
2 examples (1 passed, 1 failed)
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 60
61. Get PhpSpec to fix stuff for you
y
Method VendingMachine::availableFunds() has been
created.
expected [integer:20], but got null.
1 specs
2 examples (1 passed, 1 failed)
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 61
62. How we do TDD
Minimum code to make it work
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 62
Red Green
64. Automatically fake return values
$ vendor/bin/phpspec run --fake
Do you want me to make `VendingMachine::
availableFunds()` always return 20 for you?
[Y/n]
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 64
65. Automatically fake return values
y
Method VendingMachine::availableFunds() has been
modified.
1 specs
2 examples (2 passed)
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 65
66. PhpSpec generates code
$ cat src/VendingMachine.php
<?php
class VendingMachine
{
public function insertCoin($argument1)
{
// TODO: write logic here
}
public function availableFunds()
{
return 20;
}
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 66
70. public function insertCoins(int $coin)
{
// TODO: write logic here
}
public function availableFunds() : int
{
return 20;
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 70
71. How we do TDD
Start again
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 71
Red Green Refactor
73. How we do TDD
Example of how your code will work
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 73
Red
74. Use examples to describe behaviour
function it_accumulates_funds()
{
$this->insertCoin(20);
$this->insertCoin(50);
$this->availableFunds()->shouldBe(70);
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 74
75. Run the specification
$ vendor/bin/phpspec run
...
expected [integer:70], but got 20.
1 specs
3 examples (2 passed, 1 failed)
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 75
76. public function insertCoin(int $coin)
{
// TODO: write logic here
}
public function availableFunds() : int
{
return 20;
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 76
77. private $funds = 0;
public function insertCoin(int $coin)
{
$this->funds = $this->funds + $coin;
}
public function availableFunds() : int
{
return $this->funds;
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 77
78. How we do TDD
Make code simpler and clearer,
without changing behaviour
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 78
Red Green Refactor
79. How we do TDD
Start again
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 79
Red Green Refactor
81. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 81
function it_uses_funds_when_buying(Catalog $catalog)
{
$catalog->howMuchIsA('kitten')->willReturn(30);
$this->insertCoin(50);
$this->buy('kitten');
$this->availableFunds()->shouldBe(20);
}
Use examples to describe behaviour
82. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 82
function it_uses_funds_when_buying(Catalog $catalog)
{
$catalog->howMuchIsA('kitten')->willReturn(30);
$this->insertCoin(50);
$this->buy('kitten');
$this->availableFunds()->shouldBe(20);
}
Use examples to describe behaviour
83. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 83
function it_uses_funds_when_buying(Catalog $catalog)
{
$catalog->howMuchIsA('kitten')->willReturn(30);
$this->insertCoin(50);
$this->buy('kitten');
$this->availableFunds()->shouldBe(20);
}
Use examples to describe behaviour
84. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 84
function let(Catalog $catalog)
{
$this->beConstructedWith($catalog);
}
Let $this be constructed with
85. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 85
$ vendor/bin/phpspec run
Would you like me to generate an interface `Catalog`
for you? [Y/n]
interface Catalog
{
}
PhpSpec generates interfaces
86. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 86
$ vendor/bin/phpspec run
Would you like me to generate a method signature
`Catalog::howMuchIsA()` for you? [Y/n]
interface Catalog
{
public function howMuchIsA($argument1);
}
PhpSpec generates interfaces
87. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 87
$ vendor/bin/phpspec run
Do you want me to create `VendingMachine::
__construct()` for you? [Y/n]
public function __construct($argument1)
{
// TODO: write logic here
}
PhpSpec generates code
88. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 88
private $catalog;
public function __construct(Catalog $catalog)
{
$this->catalog = $catalog;
}
Minimum code to make it work
89. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 89
public function buy($argument1)
{
// TODO: write logic here
}
Minimum code to make it work
90. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 90
public function buy(string $item)
{
$this->funds = $this->funds -
$this->catalog->howMuchIsA($item);
}
Minimum code to make it work
91. public function buy(string $item)
{
$price = $this->catalog->howMuchIsA($item);
$this->funds -= $price;
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 91
Make code simpler and clearer
93. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 93
function it_returns_coins(CoinReturn $coinReturn)
{
$this->insertCoin(5);
$this->returnCoins();
$coinReturn->returnPence(5)
->shouldHaveBeenCalled();
}
Use examples to describe behaviour
94. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 94
function it_returns_coins(CoinReturn $coinReturn)
{
$this->insertCoin(5);
$this->returnCoins();
$coinReturn->returnPence(5)
->shouldHaveBeenCalled();
}
Use examples to describe behaviour
95. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 95
function let(Catalog $catalog, CoinReturn $coinReturn)
{
$this->beConstructedWith($catalog, $coinReturn);
}
Let $this be constructed with
96. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 96
private $catalog;
public function __construct(
Catalog $catalog
) {
$this->catalog = $catalog;
}
Minimum code to make it work
97. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 97
private $catalog;
private $coinReturn;
public function __construct(
Catalog $catalog, CoinReturn $coinReturn
) {
$this->catalog = $catalog;
$this->coinReturn = $coinReturn;
}
Minimum code to make it work
98. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 98
public function returnCoins()
{
// TODO: write logic here
}
Minimum code to make it work
99. @dave1010 - #phpsc16 - joind.in/talk/f4771 - 99
public function returnCoins()
{
$this->coinReturn->returnPence($this->funds);
}
Minimum code to make it work
100. Make code simpler and clearer
public function returnCoins()
{
$this->coinReturn->returnPence($this->funds);
}
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 100
106. Refactor
No refactoring (slow to a standstill)
R G|R_ G_|R__ G__|R___ G___|R______ G______
Refactoring (keep up pace)
R G R|R G R|R G R|R G R|R G R
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 106
107. 4 Pro tips
1. Refactor
2. Use interfaces when calling methods on
other objects
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 107
Refactor
108. 4 Pro tips
1. Refactor
2. Use interfaces when calling methods on
other objects
3. Painful to spec means you have a bad design
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 108
Refactor
109. 4 Pro tips
1. Refactor
2. Use interfaces when calling methods on
other objects
3. Painful to spec means you have a bad design
4. Refactor !
@dave1010 - #phpsc16 - joind.in/talk/f4771 - 109
Refactor
Refactor