Friday, September 21, 2012

Introduction to Unit Testing Using Python Unittest

Unit Testing is an extremely powerful tool.  It directly helps to ensure the 3 aspects of good software: Verifiability, Maintainability, and Extensibility.  Unit testing is as much a process of software design as it is a tool.  There are many great tutorials on HOW to use python unittest, and of course python documentation is an excellent resource.  I am going to focus on WHY to test your software.  Below are a couple short unit testing examples using python's built in unittest package.


Verfiability

How does one verify that their program works?  With unit testing we can create specific functions or groups of functions to target our code.  For example:

def sum_numbers(num_one, num_two):
   """return an integer, the sum of two numbers"""
   return num_one + num_two

We can easily create a test for this using python's built in unittest module.  Tests are created by subclassing unittest.TestClass

from mymodule.functions import sum_numbers

class TestFunctions(unittest.TestClass): 

    def test_add_numbers_success(self):
        self.assertEqual(sum_numbers(2, 2), 4)


On python 2.7+ running python -m unittest discover in our package will automatically search for all test.py files and run the TestClasses.  There are a couple of things to note in the above example.  All test classes must subclass unittest.TestClass.  The focal point of any test is its assertions.  The assertions are what dictate whether a test passes or fails.  Although this is a contrived example it displays how easy it is to isolate our methods and control exactly what inputs they receive!  If we were to create one test method (or more) for every method in our project we would quickly grow a test suite.  When making ANY changes to our code it becomes trivial to run through every single function in our project and verify that nothing has broken!  Imagine if we had a web app and every time we added a feature we would have to run through EVERY possible page/action!? It could take a long time doing it manually.


Maintainability

Bugs are a part of software development.  It is extremely important to minimize bugs but when they do happen it is important to create fixes very quickly.  Because bugs will occur in code it is important to have a process set up that helps to isolate the bug so that it is easy to reproduce, easy to correct and easy to verify the bug has been fixed.  Unittesting helps to do all of these.  Suppose the sum_numbers function is at the heart of a website.  It gets all sorts of user input data, and occasionally some faulty data slips through. If a string is passed as one of the parameters it will result in a TypeError!!  It is trivial to isolate and reproduce this bug.  I guess we need a little thought about what should be returned if there is an invalid input.  For this example lets return None.  We then can create another test method like:


class TestFunctions(unittest.TestClass): 

    def test_add_numbers_success(self):
        self.assertEqual(sum_numbers(2, 2), 4)

    def test_add_numbers_string_bug(self):
        self.assertEqual(sum_numbers('a', 2), None)


Running the above code reproduces the string error.  Since we haven't fixed our code yet this test will fail.  We can then change our sum_numbers method to handle a type error:


def sum_numbers(num_one, num_two):
   """return an integer, the sum of two numbers, can't trust user input"""
   try:
     return int(num_one) + int(num_two)
   except ValueError:
     return None


Running our test again will result in two passing tests.  We successfully isolated the bug, reproduced the bug and verified the bug has been fixed!! We now also have a test trail assuring us the big has been addressed.  Pretty cool.


Extensibility

With tests it becomes very easy to help an app grow.  A test suite provides a safety net for an application.  We can programatically run through every function of an app in a short amount of time.  This could take hours to do manually.   Some test suites can take hours to run, it wouldn't even be feasible to manually test a large codebase!!   As long as we keep designing our apps in a modular unit based way we can easily add functions and tests for those individual functions.  Another aspect of unittesting is how easy it is to refactor code.  Suppose we had thought it was a good idea at the time to write our original function like:


def sum_numbers(num_one, num_two):
   """return an integer, the sum of two numbers"""
   return sum([num_one, num_two])


Assuming we had the same test method as before:


def test_add_numbers_success(self):
        self.assertEqual(sum_numbers(2, 2), 4)



This test is focused on the output of our function.  It is assuring us the output is as expected.   This allows us to easily change what is happening in our function and still have the test acting as a safety net.  We can rewrite (refactor) the internals of our methods and guarentee they are still functioning in the way we originally tested them!  Our method would pass the test because it is performing the action that we want it to.  We could change the method to remove the list and sum function


def sum_numbers(num_one, num_two):
   """return an integer, the sum of two numbers"""
   return num_one + num_two


We cleaned up our function and ensures that it functions in the way we designed it to!!


Testing is a powerful tool that should be very heavily considered.  It helps verify our functions are working the way we intended them, help us easily maintain our applications and help us extend our applications.  Correct aplication design will help us isolate our problems.  Testing can take a significant amount of time, but the benefits it offers far outweigh any downsides.

Sunday, September 9, 2012

Why Unit Testing is important

One of the most controversial topics in programming is Unit Testing, or testing in generally.  There are a number of strong arguments on both sides of the issue.

For the past 10 months I have been freelancing.  During this time I have been exposed to a wide variety of code created by many individuals of vastly varying skill levels.  All of these projects have been php websites and webapps.  Of course this had led to tons and tons of different architectures.  All of these projects have had one troubling thing in common: The projects were created with absolutely no thought about maintainability.  This is, in part, because of the industry.  Boutiques and contractors are not maintaining the app.  The goal is to ship a working product in as little time as possible.  Unit testing code doesn't play into this because the time investment involved.  A significant time investment is required on determining testing strategy and writing the actual tests.  I have had many instances where writing tests takes AS LONG AS writing my actual functions!!!  Having to budget in up to 50% more time to write tests is undesirable for every party involved.

This test-less, unmaintainalbe strategy actually works pretty well (it is an industry standard) as long as the sites never need features added.   During a 3 month contract with a php boutique we had a number of recurring contracts.  This involved maintaining web sites which were created 5-10 years ago.  Many of these sites were from a different era, relying on registered_globals, and completely prone to sql injection.  So the solution is simple right? Fix the security holes, add new features, and deploy!?  No.  Many of the sites did not have any sort of structure to the programs.  Each file generally had 1 or no functions and hundreds of lines of code.  Fixing bugs meant wading through lots of code that was more or less unrelated to the problems.  Why not fix this code soup?  The issues are there are set deadlines.  People don't see refactoring the whole site into something that can be maintained as a good usage of time.

Writing code as if it were going to be unit tested will resolve many issues.  Unit testing is important because it helps us think in terms of maintaining code.  The most important aspects of unit testing are usually overlooked:

Thinking about program structure.  This NEEDS to be thought about.  Even for small websites.  Sitting down and writing whatever comes to ones head is a sure way to reduce the quality of code and to remain a mediocre programmer.


Designing units, what are logical sections

I think a simple way to do this is to go through long code and comment what each section does.  For example

// log in a user
// check user permissions
// get friends of user
// etc

When doing this it becomes very very apparent what should comprise a "section".  This would makes sense to have a login_user function.  Or a check_permission function.   Even if they are only used one time, it still makes sense to create functions for these.  This helps with the next point.

Thinking in terms of maintaining code.
    - how willl this code be added to?

Actually thinking about program design will make maintaining and extending your code so much easier.  Say that in addition to the facebook login that version 1 of the site uses a client wants to add twitter auth too.  With a login_user function this is pretty easy.  All login code can be located in this function.
 

Learning to create software as a series or related units takes practice, but it pays off in the long run as code is easier to work on and easier to extend.