2. TDD Walkthrough - The Time in Words
Hello and welcome to my TDD Walkthrough!
Today’s exercise is The Time in Words, taken
from HackerRank.com at the following URL:
https://www.hackerrank.com/challenges/the-
time-in-words/problem
3. TDD Walkthrough - The Time in Words
In these TDD Walkthroughs, I assume that my
readers have basic knowledge of test driven
development. I highly suggest having an
understanding of TDD before going through this
walkthrough. The following blog post explains
the cycles of TDD.
http://blog.cleancoder.com/uncle-
bob/2014/12/17/TheCyclesOfTDD.html
I also assume that my readers have knowledge
of refactoring tools and terminology. If you need
more information, this blog post on the six core
refactorings will provide enough information.
http://arlobelshee.com/the-core-6-refactorings/
4. TDD Walkthrough - The Time in Words
For this exercise, I have the following already set up:
Chrome browser
Visual Studio Code
Jasmine testing framework
(see my other blog on setting up: https://www.greatersum.com/tdd-setup-jasmine/)
With the above already set up, let’s get started!
5. TDD Walkthrough - The Time in Words
I would normally brainstorm a test list of all the functionality I expect out of my
code. Since the exercise provides good examples of that functionality, I will use
those examples as my test list.
My first step will then be to implement the simplest test case, and for that I choose
5:00, which translates to “five o’ clock”.
Before that though, we must set up our project. Creating files, pointing to file
locations, and running for the first time.
6. TDD Walkthrough - The Time in Words
Starting with a basic Jasmine standalone
project, I will create the file that contains my
tests.
7. TDD Walkthrough - The Time in Words
There are various conventions for file names out
there. I tend to use the affectionately dubbed
kabob-case: the-time-in-words.spec.js
8. TDD Walkthrough - The Time in Words
This is a spec file. This file needs to be included
in the Jasmine spec runner, so open up the
SpecRunner.html file next.
Look for the comment that says “include spec
files here”, and include the spec file you just
created.
9. TDD Walkthrough - The Time in Words
Let’s write our first test. First, we create the test
suite:
And then we write the test function:
In our test function, we write what we are
expecting:
10. TDD Walkthrough - The Time in Words
At this point, you should be able to run your test runner HTML file by opening it in
the browser, and see a failing test.
Failing the test here is good! You haven’t written any production code yet, and
that’s ok. What you did write is a test that will tell you when your code fulfilled a
purpose, and that is to translate 5:00 to five o’ clock. It’s not much, but we take
one step at a time.
11. TDD Walkthrough - The Time in Words
Ok, so what does the test runner say?
theTimeInWords is not defined. That makes
sense, we haven’t written anything called
theTimeInWords yet.
Let’s go make that function!
12. TDD Walkthrough - The Time in Words
To keep the code organized well, the tests are
contained in the spec file, in the spec folder.
So then our source code should go in the source
folder!
13. TDD Walkthrough - The Time in Words
Our new source file will need to be included in
the Spec Runner though, so let’s not forget to do
that.
14. TDD Walkthrough - The Time in Words
Now back to our source file. The first issue is
that theTimeInWords is not defined, so let’s go
define it.
15. TDD Walkthrough - The Time in Words
You might be tempted to go implement all of the function right now. But bear with
me and take one small step at a time. Let’s focus on the error at hand, and we’ll
tackle each issue as they come up. That way we don’t write extra code for no
reason.
This is, after all, development that is driven by testing. So run the tests again. You
can rerun the tests by refreshing the spec runner page.
16. TDD Walkthrough - The Time in Words
Ok, different error message. Expected
undefined to be ‘five o’ clock’.
Well, the solution sounds simple. Make our
function return five o’ clock!
17. TDD Walkthrough - The Time in Words
This solution will make you cringe. It makes me cringe.
But you never write more code than you need. That prevents you from wasting
time on extra code, and it prevents you from writing code that can break other
things in other ways.
Focus on minimal design, and drive your code towards the functionality you want
with tests.
18. TDD Walkthrough - The Time in Words
Ok, that being said, our current test should pass.
Side note: We could implement all of the o’
clocks from 1 to 12. But in the interest of
keeping the blog shorter, I will focus on the
examples on the test list I decided on in the
beginning. I will still keep the code in a flexible
manner to allow for implementation of hours 1 to
12.
19. TDD Walkthrough - The Time in Words
So, with the five o’ clock case done, let’s pick
the next case. Preferably an easy one, like 5:30.
Our new test will fail, while our previous test is
still passing as shown in the top left of the test
runner. This is also important! Whatever you do
to your code, don’t break the old tests while
getting the new one to work!
20. TDD Walkthrough - The Time in Words
So, that being said, how do we make our
function do both of these? Looks like an if
statement will solve that problem.
And run our tests again, they should be passing
now.
21. TDD Walkthrough - The Time in Words
We have an opportunity to make our code cleaner! Whenever your tests are
passing and you are in a good state, you can look back at your code and look for
ways to improve it.
Removing duplication is one of those ways to clean up code. Duplication here
means using the same thing or doing the same thing in more than one place.
22. TDD Walkthrough - The Time in Words
Where is the duplication?
We use the word ‘five’ in both our strings. That
word will change when we do other hours in the
day, so this looks like a good opportunity for
refactoring.
23. TDD Walkthrough - The Time in Words
Extract the ‘five’ in the strings to a local variable.
Rerun those tests. They had better still be
passing! As long as they are passing, we can
continue.
What next? Let’s translate 5:15 to quarter past
five. Write the test first. Make sure it is failing.
24. TDD Walkthrough - The Time in Words
Much like last time, we can write an if statement
for the quarter past five case. Thanks to our
refactoring from before, we can reuse the hour
variable for our return.
Tests are passing again! Is there an opportunity
to clean the code?
25. TDD Walkthrough - The Time in Words
The return from the ‘half past’ and the ‘quarter
past’ look awfully similar. The only difference is
the ‘quarter’ vs the ‘half’. Let’s extract the ‘past’
out to local variable, as a step towards cleaning
the code.
We have changed the code, so we must run the
tests! They are still passing, so we may
continue.
26. TDD Walkthrough - The Time in Words
There is a type of duplication here as well! The
‘quarter past’ scenario and the ‘half past’
scenario look rather similar, the difference is in
the minutes past the hour.
The way to cleaner code may not be obvious
here. There is a duplication of intent in terms of
the minutes past the hour. Let’s turn the ‘quarter’
and the ‘hour’ into variables, and maybe the way
to cleaner code will become more obvious.
27. TDD Walkthrough - The Time in Words
How about that? They return just about the
same thing. Let’s move the returns down, to
remove the duplication.
Ah, but there is a problem. The return for the
hour o’ clock scenario conflicts if we move the
minutes past hour return down. Let’s find a way
to move the hour o’ clock return up so it doesn’t
conflict. Will an if statement change anything?
28. TDD Walkthrough - The Time in Words
Make the change. Run the tests. Everything is
green. The hour o’ clock if block can be moved
up now, with less fear of breaking things.
29. TDD Walkthrough - The Time in Words
That move didn’t break anything. Running our
tests will tell us that.
Now we should be able to move the minutes
past hour returns down without conflict.
30. TDD Walkthrough - The Time in Words
Quite a few changes were made. Run the tests,
make sure they pass, and continue on.
What should our next test be? Since we are
returning minutes past hour, we can probably
implement one minute past and ten minute past.
Let’s start with one minute past.
31. TDD Walkthrough - The Time in Words
Run the test to see it failing. Now, how to solve
this? An if statement would solve this problem
easily.
I’ll put the one minute past scenario between the
hour o’ clock and the quarter past scenario, so
that the scenarios are in ascending order by
minutes.
Organization does make code easier to read.
32. TDD Walkthrough - The Time in Words
Ok, tests should be passing.
But now our code doesn’t look so good with so
many if statements. We should clean those up.
What can we do about them? For one thing,
they only seem to care about the minutes. Let’s
change the if conditional to reflect that. The
javascript slice function will work here.
There is a pattern here. If the minutes of the
input is this, minutes is set to that. There are 3
pairs of this pattern.
33. TDD Walkthrough - The Time in Words
This pattern can be refactored into a key/value
type data structure. Some people call it
dictionaries, some people call it look ups. In
javascript, we can just use an object. Write up
the object first with its pairs.
Then replace the minutes strings with the
appropriate key from the minutes conversion
object.
34. TDD Walkthrough - The Time in Words
What I am doing here falls under a technique where you first create the
duplication, migrate your code from one version to the other version, then remove
the duplication.
In doing so, you migrate your code from one structure to another structure in a
step-by-step process. This method lets you catch errors while they are small. Feel
free to run tests continuously while making the changes.
35. TDD Walkthrough - The Time in Words
Now all these if statements look about the same!
Who needs the if statements anymore. Just
remove them.
36. TDD Walkthrough - The Time in Words
Pretty impressive, how so much code collapsed
into the one line. And all of this was done in an
incremental manner using certain techniques.
But wait, there’s more! What happens when we
try to add the next test case, ten minutes past
five?
37. TDD Walkthrough - The Time in Words
How much code would it take to pass this test?
Just a key value pair. Our code is very clean
and easy to change. That’s good news,
considering that our next few test cases will be
more interesting. Let’s do quarter to six first.
38. TDD Walkthrough - The Time in Words
Write the test and see it fail.
Quarter to six doesn’t seem to fit well into our
current code. Looks like we’ll have to write an if
statement for this. We’ll figure out how to make
it cleaner later.
39. TDD Walkthrough - The Time in Words
Tests should be passing now, but our code isn’t
as clean as before. We can take some small
steps to making it cleaner. We can change the
conditional to check minutes. And we can add
45 minutes as a key value pair.
It’s a small improvement. Having something
better than before is fine. Our upcoming test
cases will make the cleaner code more obvious
too.
40. TDD Walkthrough - The Time in Words
Tests should be passing now, but our code isn’t
as clean as before. We can take some small
steps to making it cleaner. We can change the
conditional to check minutes. And we can add
45 minutes as a key value pair.
It’s a small improvement. Having something
better than before is fine. Our upcoming test
cases will make the cleaner code more obvious
too.
41. TDD Walkthrough - The Time in Words
Let’s do 5:40, and run the tests to see it fail.
We can use some of our existing structure for
this scenario. We can add 40 to the minutes
conversion, and we can use our 45 minute
scenario to implement the 40 minute scenario
too. Not bad, but still a little bulky looking. Let’s
clean up a bit. When do we use the minutes to
six logic? When minutes is past 30. So let’s
replace the if conditional with that.
42. TDD Walkthrough - The Time in Words
Make the change. Run the tests and make sure
you don’t break anything. Now that’s better.
What other patterns are there? When minutes is
past 30, you say ‘to’ and ‘six’. Otherwise you
say ‘past’ and ‘five’. That sounds like something
we can implement with variables and logic.
43. TDD Walkthrough - The Time in Words
At this point, the return inside the if block looks
quite similar to the return at the end. Let’s make
them exactly the same. Then remove the return
from the if block. Theoretically everything should
still pass!
And they do pass! Our code doesn’t look so
bad, and we’ve hit all the major test cases. The
remaining test cases fit into our code very well.
Just add some key value pairs to the minutes
conversion object.
44. TDD Walkthrough - The Time in Words
Now, there is more duplication to remove and other refactorings to do to improve
the code quality, but I will leave that to you guys. This walkthrough illustrates how I
solve this problem in somewhat small steps. What I didn't dive into was why, or
how I knew that step was the next small step. I believe that the whys and hows
are best acquired through personal experience. So I encourage you guys to get
out there and try it! Good Luck!
45. TDD Walkthrough - The Time in Words
For those who want to look at the source code, I have this walkthrough posted on
github here:
https://github.com/khapeterk/TDDWalkthroughTheTimeInWords