When we were in Madrid last year, a book was accompanied me – the new edition of Clean Code authored by Uncle Bob. He describes how the professional programmer acts, works, talks and develops himself. Robert C. Martin claims an example from his own life – a quick, 15 minutes, code kata done twice a day. I think this requires a lot of discipline which might be hard to achieve at the beginning, so I propose you to schedule one hour a week to learn something new. To help you I’d love to start the same. I hope to share my ideas, kata scenarios, links, tools, and the process with you and I hope you would do the same.
So, let’s start. The main movement of doing code kata is around the Test Driven Development, so today’s topic will go around PHPSpec and design patterns. The second is always important, no matter what. I’ll go with you from the beginning of exercise and help you with installing, setting up and kickoff, describe you excersise in shortened version and do a first step with you.
PHPSpec
PHPSpec is a tool which can help you write clean and working PHP code using behaviour driven development (BDD) or TDD. TDD and BDD are techniques derived from test-first development.
The value of using an xSpec tool instead of a regular xUnit tool for TDD is the language. The early adopters of TDD focused on behaviour and design of code. Over time the focus has shifted towards verification and structure. BDD aims to shift the focus back by removing the language of testing. The concepts and features of the tool will keep your focus on the “right” things.
In practice – first of all your write tests (a small piece), run PHPSpec which generates missing classes, methods and files for you and write an implementation.
Installation
1 2 3 4 5 6 7 8 9 10 11 12 13 | echo '{ "require-dev": { "phpspec/phpspec": "~2.0" }, "config": { "bin-dir": "bin" }, "autoload": {"psr-0": {"": "src"}} } ' > composer.json curl http://getcomposer.org/installer | php php composer.phar install |
Those will (1) create a composer.json file with added phpspec in second version, download composer and install. After that, we have everything what we need to start, except the exercise description. The composer might be quite slow, so we have time to read the requirements.
Prices – Code Kata
The full description can be found on Kata01: Supermarket Pricing. I will simplify the example just a little bit.
Goal
There is no doubts that we do application for business and the business wants to earn money. It’s a common problem – how to calculate prices. You can have a regular prices, with different level of taxes, with discounts or without, calculated per piece or per weight.
The goal of this kata is to practice a looser style of experimental modelling. Look for as many different ways of handling the issues as possible. Consider the various tradeoffs of each. What techniques use best for exploring these models? For recording them? How can you validate a model is reasonable?
We need to create an extensible architecture, but I would like to make the task harder – let’s base this architecture on at least one known design pattern and call it. I believe that consolidation of knowledge can be achieved by exercises.
Our preliminary requirements:
- Regular price per piece
- Regular price per weight
- Taxes on different levels (8%, 12%, 17%, 23%, depends on article type)
- Discounts (system discounts or custom discounts)
- buy two, get one free (so does the third item have a price?)
- three for a dollar (so what’s the price if I buy 4, or 5?)
- how to add shipment cost in the future?
- others?
There might be more options, of course.
First step with PHPSpec
Well, my composer just finished and we can start. I will create a base class for our calculations and call this Price. How do I do that? Not by creating file, but by creating a description of class – that’s how the test class is called in PHPSpec.
1 2 | $ bin/phpspec desc Kata/Price Specification for Kata\Price created in spec/Kata/PriceSpec.php. |
I can open the spec/Kata/PriceSpec.php file. I should see:
1 2 3 4 5 6 7 8 9 10 11 12 | namespace spec\Kata; use PhpSpec\ObjectBehavior; use Prophecy\Argument; class PriceSpec extends ObjectBehavior { function it_is_initializable() { $this->shouldHaveType('Kata\Price'); } } |
It is our first test in PHPSpec (!!!) which checks if the Kata\Price class exists. But… it doesn’t. I mentioned that the PHPSpec will create that for you. How does it know when your class or method should be created? When you run tests and it cannot be found.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | $ bin/phpspec run Kata/Price 10 - it is initializable class Kata\Price does not exist. 1 specs 1 example (1 broken) 77ms Do you want me to create `Kata\Price` for you? [Y/n] y Class Kata\Price created in src/Kata/Price.php. 1 specs 1 example (1 passed) 100ms |
The file src/Kata/Price.php was created and looks:
1 2 3 4 5 | namespace Kata; class Price { } |
Do small steps – create a class, run tests, start with a small method, run tests, check first assert in test’s method, run test…
Your first assertion
I will add a method in PriceSpec:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?php class PriceSpec extends ObjectBehavior { function it_is_initializable() { $this->shouldHaveType('Kata\Price'); } function it_has_regular_price_per_piece() { $this->setPrice(10); $this->getPrice()->shouldReturn(10); } } |
Now, run the bin/phpspec run and we notice that the first test fails. We are in the “RED” section of tests and should make it “GREEN”. Go to the Price class and find all `// TODO: write logic here` sections:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Price { public function setPrice($argument1) { // TODO: write logic here } public function getPrice() { // TODO: write logic here } } |
Implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Price { protected $price; public function setPrice($price) { if ( ! is_numeric($price)) { throw new \LogicException('Price should be numeric'); } $this->price = $price; } public function getPrice() { return $this->price; } } |
And execute bin/phpspec run once again. Ha! All tests passed, we can go forward.
Taxes
1 2 3 4 5 6 7 | //class PriceSpec function it_has_regular_price_with_tax() { $this->setPrice(10); $this->setTax(8); $this->getPrice()->shouldReturn(10.8); } |
bin/phpspec run
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <?php class Price { protected $price; protected $tax = 0; public function setPrice($price) { if ( ! is_numeric($price)) { throw new \LogicException('Price should be numeric'); } $this->price = $price; } public function getPrice() { return $this->price * (1 + ($this->tax / 100)); } public function setTax($tax) { if ( ! is_numeric($tax)) { throw new \LogicException('Tax should be numeric'); } $this->tax = $tax; } } |
bin/phpspec run and every test should pass.
My time is over
My time is over, your probably not. I wrote the most simple examples I could. The code, the tests are not pretty, but there are no right or wrong answers in the kata: the benefit comes from the process, not from the result. If you exersise a lot, your code will be better and better.
Excuse (there always will be some excuses) – I needed to write this post and do the kata in the same time :P.
On the very top you can find a photo of Masutatsu Oyama – an inventor of karate kyokushin. The main exercise in karate is kata – a sequence of movements, positions, kicks and punches purposed to develop your fighting skills. You exercise technique very carefully and focus on every single element which makes you able to cut off the bull’s horns – literally.