Humblecoder

Caution, this blog may be ironically named

Why Unit Testing Is Essential When Learning a New Language

| Comments

I’ve been spending a bit of time lately learning Python and Pythonista ways, although a lot of the stuff I’ve read is more about the how of dynamic languages not the why but that’s another discussion.  I find the best way to learn a new language is to pick a pet project and run with it. As random as it may seem this fits in nicely with my new years resolution of losing weight.  So I started a Diet Tracker website using the Django framework.

In my continuing quest to use and learn more about Test Driven Development one of the first things I spent time diving into (after the basic syntax) was the built-in unit test framework in Django, the Python standard library has one too but the Django one is richer.  It paid off quite quickly.

One of the key parts of the site was the ability to track your changing weight.  The first version of the weight display page is sortable in ascending or descending order and can have a selectable number of entries.  Also, weights will be stored in the model as KG but displayed in stone and pounds, this will be made user configurable at a later date.  The exceedingly simple algorithm for this page is:

  1. Accept a list of weights
  2. Reverse the sort order if we need it to be newest first
  3. Take X number of items
  4. Create a new list of the display adapter class

I would write this in C# as something like:

public List<DisplayWeight> ConvertToDisplayWeightList(List<ModelWeight> weights, int numberForDisplay, bool newestFirst)
{
    if (newestFirst)
    {
        weights.Reverse();
    }  
    List<DisplayWeight> returnList = new List<DisplayWeight>();
    weights.GetRange(0, numberForDisplay).ForEach( mw => returnList.Add(new DisplayWeight(mw)));  
    return returnList;
}

This does look WTF-esq but it’s not that bad once you understand how Django’s ORM works.  The Python I wrote to do this was:

def get_display_weight_list_by_date(self, numberToLoad = 5, newest_first = True):
        currentWeightList = Weight.objects.all().order_by('date')  
        if newest_first:
            currentWeightList.reverse()  
        finalList =[]  
        for w in self.currentWeightList[:numberToLoad]:
            finalList.append(DisplayWeight(w))  
        return finalList  
def testLoadWeights_5items_newest_first(self):
        "Checks we have 5 weights returned and the newest is first"
        loader = DisplayWeightLoader()
        loader.get_display_weight_list_by_date(numberToLoad=5, newest_first=True)  
        self.assertEquals(len(loader.currentWeightList), 5)
        self.assertEquals(loader.currentWeightList[0].date, datetime.date(2010, 01, 19))  
def testLoadWeights_10items_oldest_first(self):
        "Checks we have 10 weights returned and the last is first"
        loader = DisplayWeightLoader()
        loader.get_display_weight_list_by_date(numberToLoad=10, newest_first=False)  
        self.assertEquals(len(loader.currentWeightList), 10)
        self.assertEquals(loader.currentWeightList[0].date, datetime.date(2010, 01, 9))

The function is not very pythonic and a bit WTFy, I’ve rewritten it since, but the second unit test failed.  Without going into where the test data is coming from or what is going on, the test is failing because the newest date is still first.  Why is it failing? Because I made an assumption.

The assumption?  That an instance method on an instance of a mutable object affects the state of that object.  I think it’s a fair assumption and it is the way that C# works.  But it turns out not to be the case in Python, the line at fault:

currentWeightList.reverse()

It should be

currentWeightList = currentWeightList.reverse()

There is a couple of very important lessons here:

  • Never make assumptions: Yet more proof that assumptions make an ass out of you and me.

  • Reading language and library documentation is extremely important:  I had skimmed over the tutorials and docs for the standard Python library and I thought I knew enough to get up and running.  I should really have gone back and read the documentation properly for the functions I’m using for the first time.

The most important lesson though:

Unit testing is not an advanced skill or topic, it is essential and part of the basics 

Unit testing is not part of the standard Python docs tutorial, it is at the end of the Django tutorial (to be fair it is mentioned briefly early on) and barely gets a mention on MSDN.  Implying that it’s not that important and it’s something you can leave to later.  I think that it is extremely important, whether you’re an experienced developer trying to get to grips with a new language or a beginner trying to get to grips with programming it is the single most important thing after learning the syntax.

At every level it helps you to think about what you’re actually trying to achieve, gives you instance feedback to your understanding of a feature, language or library and helps you to write small simple functions.

Comments