|P is for Practical|
A few months back I bought the book "The Pragmatic Programmer" and read it from front to back and felt it a great addition to my book collection. I have applied many of things they talk about and have found that my performance is improving.
Last year in October I had started down the road of OO everything, some of you can hopefully relate. So all this good OO thinking was going about and I had "improved" my line count, abstraction (to some extent) and code reuse, but I was just not getting where I thought I should be in volume of code produced. In fact I felt it was more difficult to write my (at the time ) throw away "test" scripts.
Last month I started using Test::More with more seriousness. I started using the "Don't test the big end, test the small end" mentality. That is test the smallest amount of code you can and then work your way up. This helped me find ways to split apart different sections of my existing modules into much simpler methods that performed part of, rather then all of the work.
Now I don't have anyone to answer to for the appearance or style of my code, except for myself and I think my code should improve even if the clients will never look at or understand what I have done. So these improvements ( or perceived improvements, time will tell ) are all self driven and constructed from various points of exposure. While I didn't reference any material directly while writing this, I can't say that I didn't paraphrase.
I had thought for sometime that writing test code was a difficult annoying task ( lack of information, I had never really tried to write real tests before ) that was only helpful if you had 10_000+ lines of code to write. How wrong could I be. I took some time and invested in getting past the learning curve on some of the various Test modules that are available and this is what I found:
I settled on Test::More for most of my testing scripts. With most of the Test modules it is important to remember 1 (one) key thing. True or False
Thats it! Thats all you are testing. Is it or is itn't. Once I saw that is all I was really doing I started liking to write tests. Now there is more you can apply to the mentality and practice of testing, but I think if you are just starting out keep it simple.
Here is simple testing script that runs 6 tests and fails two of them:
So how that evolve into better code? Lets take a more complicated process and see how we can improve it.
Our initial method "requires" that it returns the content of a file that is passed to it. We don't do any file type checks "assuming" |-) this is a semi private method.:
Now to test that code we do something like this:
Well that worked out OK , but I noticed somethings during my testing. There was no easy way for me to get back the file name, since I might want to do something to the contents and write it back out to the original file. It also assumed I would be passing in a hashref which is over kill if I know the name of file already, which I most likely will at some point in the testing. This is how I come to the coding to test part. I want to modify this method so my testing is easier and at the same time it has the side effect of making the code useful in more places, which increases the likelihood of me reusing it for other things.
So I do two things, make it so the method will return a list of items, the content and the file name or just the content based on the request type and it will allow for a normal string to be passed in which we will assume is the file name.
Lets refactor our test code slightly to test the new features.
After looking at how much testing code there is you might be thinking that you would be spending all your time writing test code. While the volume of test code SHOULD be greater then the "worker" code it is by no means as difficult to write. Since all you are doing is testing each possible condition under which your worker code will perform you are just testing for true and false on several "real problems" and only making potentially small problems. I say problems because testing scripts are prone to bugs as well, but since not nearly as much complex thought is going on in them you should be able to quickly determine if it is your test or your worker code that is wrong.
The other benefit of writing tests is the ability to easily check for broken code along the way, aka regression testing, or in other words does it still do x when you add y. Your client/boss calls you and says that they need feature xyz installed. Now you can add another test for its specific functions and rerun your test script to at least be some what confident that you haven't made a mess of things.
UPDATE: missed some semi colons in my code blocks, maybe I should have tested it, how ironic :) and added a readmore tag.