I have officially spent more time testing and refactoring my code for my black jack game, RubyJack, than I have on the original code base and it has taught me a big lesson: TEST FIRST.
This is a topic that I don’t have the breadth of knowledge to really attack. Jeff Atwood does a much better job than I, here. I do, however, feel that I can add the perspective of a new programmer.
The challenge of testing first as a n00b, is that when you are learning to program, you generally learn
def print_hello_world puts "hello world" end print_hello_world
before you learn
it "prints hello word" do print_hello_world.should == "hello world" end
I wouldn’t say this is a mistake since testing is inherently more complicated, but it does create a significant barrier to entry to the new programmer. This barrier is mainly a comfort level thing.
When I was first learning about testing, I thought “I can really see the theoretical application of this, but my programs are so simple now that I don’t need tests.”
BIG F&%$ING MISTAKE.
The big example, in RubyJack, of where writing my tests first would have helped me, was during the refactoring process. The first draft of my code may not have been bad for a new programmer, but was(is) poorly written software on a whole. There were methods in classes where they didn’t make sense, arrays of strings where they should have been arrays of objects, 25 line methods that did one simple task and many, many other problems.
Up to this point my tests consisted on running the program through my command line, playing the game till it broke and reading the error message. Not super efficient.
So, after I published the first version of the gem (gem install RubyJack) I started to move stuff around. First, I did some simple refactoring of individual methods, no problems. Then I thought “oh, well this method that starts the game probably should be in the ‘Game class’ and not the ‘Player class.’”
All Hell Broke Loose.
Nothing worked anymore, I couldn’t even start the game let alone deal a card or initialize a deck.
Thank god for “git reset –hard”.
I was able to hop back to my latest commit and start again (more about using commits to your advantage later). Then, I wrote some tests. It was hard; it hurt my head; I didn’t like it.
I struggled through them and got (most of) them to pass. Around this time I brought in some professional help, Gavin. Gavin was nice enough to sit with me and help me do a little (a lot) of refactoring of my code. We moved some methods into classes where they made more sense, and created classes that should have been there from the beginning.
Then I tried to do some more refactoring on my own, which led to even more problems. I hadn’t learned my lesson.
Once again, I found myself reaching for “git reset”
It was then that I resolved to write better tests. I re-watched the pragmatic studios video on unit testing and peppered Gavin with some more questions. Then, I wrote a bunch of tests and MADE THEM ALL PASS.
When I originally wrote my tests, if I couldn’t get a test of a certain functionality to pass, I marked it as “pending” instead of figuring out the problem. Not this time. It took me hours, and until 2am, but I made them all pass.
I then entered the process to which Gavin introduced me to:
What that translates to is this:
Check my tests, if they pass I make one small change. I then check my tests again, some of them may be failing, I want to make sure they are the ones I expect. Then, I continue tweaking the code till I get it to pass all the tests again (sometimes tweaking the tests too).
Then I start the process all over again with my next change.
The benefit to testing first, as I see it, is as it concerns the thought process towards your code. As J. Timothy King States:
You ask, “What code will I write to generate a solution?” But that’s backward. The first thing you should be doing… The first thing you ask is not“What code will I write?” The first thing you ask is “How will I know that I’ve solved the problem?”
So, my tip for other n00bs out there is to learn to test sooner rather than later. You won’t regret it.
I want to give a huge public thank you to Gavin Stark for helping me with the refactoring process and answering all my crazy n00b questions. Thanks Gavin! Also, thanks to Aubrey Goodman who graciously takes my questions at all hours. Finally, I’d like to say thanks to Andy Lindeman who went through and commented on my code on github! It is people like you guys that make the Ruby community awesome and make me glad to be a part of it.
Disclaimer, links, and tweets
I love to learn and I do not pretend to be an expert on the topics I post about. If I’m wrong please, let me know in the comments, I love feedback and you won’t offend me. Follow me on Twitter; I love to discuss Ruby, books and anything interesting.
If you want to help an aspiring Rubyist, I’m on GitHub and would love any feedback you have to offer.
Many people have helped me learn, I would love to return the favor! Get in touch and I’ll see if I can help.
Thanks for reading!