Monday, December 20, 2010

A case for testing getters, setters and properties.

Conventional wisdom suggests that getters and setters (a.k.a. properties) are simple enough that testing them is unnecessary unless some branching logic has been introduced. This is advocated in any number of books on the subject of unit testing. This is what I thought as well. After all, most properties simply assign and retrieve a value without any manipulation.

Today I discovered a reason that may make it worth while to test even these. I spend most of my time working with legacy code. Often times I am uncomfortable making changes without the safety of a test harness so I'll take advantage of automatic test generation to create skeleton tests from production code that I can fill in as I work.

The tool I use for this isn't terribly smart. It just creates a class of test cases for each public method it finds in the production class its given. Each test case calls each method but leaves the Assertion up to the developer to fill in.

I had just created a new test harness from a production class I needed to modify. I ran the tests without any changes, meaning no checks were being done. I fully expected the tests to succeed since the framework I was using didn't consider an assertion-less test to be a failure. I was amazed when it did in fact fail.

The reason for the failure was a stack overflow caused by a getter returning the value of the property it was as a backing function for rather than the value of the backing field. This of course resulted in an endless recursion that quickly exhausted the call stack. This bug had never turned up in production because this was a base class that wasn't used directly and all descendant classes overrode that particular getter.

This subtle bug is more likely to happen in languages like C#, Object Pascal and Python (and several others) which hide the getter and setter calls behind a more friendly syntax that allows them to be assigned like public fields.

MyObject.PropertyValue = 123;

So even before any of the tests were verifying results they had already uncovered a bug in one of the simplest operations possible.

Friday, August 20, 2010

Bad Programming Practice #2

Misusing CTRL+V

It seems innocent enough. You write a snippet of code that causes tulips to spring up all over the computer screen. The users rejoice. Some time later they decide they want the option to see sunflowers as well. They're both flowers and your time is precious so you decide the quickest way is to copy the tulip code, and replace tulip with sunflower. Problem solved. The users rejoice again.

The years pass. Each time the users request another type of flower: CTRL+C, CTRL+V, Modify. Then one day... you discover your original tulip desktop function has the ugly side effect of slowly filling the user's hard drive with temporary files in random locations until it chokes. Only now you have 500 modifications of the same function, many of which barely resemble the original and they all have the same problem. It takes months to fix and even after you release Flower Desktop 10.0 SP1 you're still not sure if you found them all. Mean while, your users grow tired of manually hunting for randomly placed temporary files while waiting for you to fix the problem and decide to replace your program with the cool new Flaming Desktop 1.0 they just finished downloading. You flog yourself repeatedly for copying and pasting to save time when it would've only taken slightly longer to modify your tulip function to take the type of flower as a parameter. Saving you all this work hunting down evil clones of ShowTulips().

Wednesday, August 18, 2010

Bad Programming Practice #1

Hard coding a path that only exists on your development machine

MyPath = "C:\Foo\Bar";

Let's faced it. This is just lazy. It doesn't matter if you are the only one working on the project. You will eventually move to another machine or someone else will be added to the project. Next thing you know you're pulling your hair out trying to figure out why it works on one machine but not the other. By hard coding the path you've created a dependency on the exact folder structure of your hard drive.

What's worse is that you're not only forcing this on yourself or another developer but possibly the end-user as well. Save everybody some frustration and use paths relative to the main executable or if warranted, make the path configurable by the end user.