Better Practice, Dec. 2018

(singke) #1
http://www.phparch.com \ December 2018 \ 23

How to Learn PHP Unit Testing With Katas

Great! We have a real failure of the
function under test. So we can go
on and write the function body. You
might already suspect what the form
of the final solution for a Roman
numeral converter is. If so, you may be
unpleasantly surprised my first “green”
production code is this:

function arabicToRoman($arabic)
{
return 'I';
}

I mentioned this above, but the prac-
tice of katas and TDD dictates we write
as little production code as possible to
get a green. This code gets a green when
we have a single test. So while we could
refactor after getting here, we really
shouldn’t.

Your Next Few Tests


After you’ve gotten a pass at 1 , you
should probably go to 2 or 3. I usually
skip 4 (which is the weird subtractive
thing of IV) and instead do 5. Six—and
maybe seven— start to require us to
generalize a bit. testThat10IsX gets us
to code which looks a lot like 5. So we’re
starting to find the pain of the duplica-
tion that a sheer “drivel out” version has
led us to.
It’s around the point of having
specific if or while blocks for 1, 5, and


  1. Often I’ll go past 20 and to 30. At
    this point, I’ll feel enough pain I will set
    about refactoring.


The Interesting Twist:


Refactoring Time!
At the point I start to want to refactor
(depending on how intentionally obtuse
I’ve been to that point), my production
code reaches this point looking like
Listing 2.
Pretty ugly, but it’s passing the seven
tests I have: 30, 20, 10, 5, 3, 2, 1. Now,
the magic of testing and refactoring
comes in.
Because I’m green now, I can freely
make changes to this code. In general,
my goal right now is I can sense there’s
a whole lot of duplicated code which
doesn’t look uniform. I like to make
the similarity very obvious first. So I’ll

make a series of changes favoring while
loops over both the if and for I’ve got
running right now. That’ll lead me to
something like Listing 3.
I’m skipping steps here, but I think
with your test harness you’ll find it
pretty easy to guide yourself here, and
give yourself the confidence it’s all still
working.
Now the pattern is so clear you’d have
to be trying to miss it: we have a set
of pairs X and 10 , Vand 5. If and when
we combine them, we’re set for the big
transition where we can write down
this lookup table into an associative

array (yay PHP!), and then foreach
through it with a while loop. Which
leads to Listing 4.
When I run my tests against this code,
I see my fourth or fifth straight green
during this refactoring. If at any point
you get a red while refactoring, it’s a
chance to undo to stay green.
With my test and this structure, it’s
debatable whether or not I must test-
drive the next phase of the code. I
suspect if we fill in 4 between 5 and 1, it
will work. I suspect if we extend upward
to the fact that C is 100, it will work. We
should probably test at least a few of
them on the way there though.

Listing 2


  1. function arabicToRoman($arabic)

  2. {

  3. $result = '';



  4. if ($arabic > 9) {

  5. while ($arabic > 9) {

  6. $result = $result.'X';

  7. $arabic = $arabic - 10;

  8. }

  9. }



  10. if ($arabic > 4) {

  11. $result = $result.'V';

  12. $arabic = $arabic - 5;

  13. }



  14. for ($i=$arabic; $i > 0; $i--) {

  15. $result = $result.'I';

  16. }



  17. return $result;

  18. }


Listing 3


  1. function arabicToRoman($arabic) {

  2. $result = '';



  3. while ($arabic >= 10) {

  4. $result = $result. 'X';

  5. $arabic = $arabic - 10;

  6. }



  7. while ($arabic >= 5) {

  8. $result = $result. 'V';

  9. $arabic = $arabic - 5;

  10. }



  11. while ($arabic >= 1) {

  12. $result = $result. 'I';

  13. $arabic = $arabic - 1;

  14. }



  15. return $result;

  16. }


Listing 4


  1. function arabicToRoman($number) {

  2. $arabicToRoman = [

  3. 10 => 'X',

  4. 5 => 'V',

  5. 1 => 'I',

  6. ];



  7. $result = '';

  8. foreach ($arabicToRoman as $arabic => $roman) {

  9. while ($number >= $arabic) {

  10. $result .= $roman;

  11. $number = $number - $arabic;

  12. }

  13. }

  14. return $result;

  15. }

Free download pdf