Unit-testing

What is unit-testing?

  • Unit testing is a method by which individual units of source code are tested to determine if they are fit for use.
  • Also a means of automatic code walkthrough.

Why unit-testing?

  • It makes sure that you code is syntactically error-free
  • It helps in resolving complex logical issues.
  • It leads to modular, extensible code that is easy to test, understand, and maintain.
  • If you stack together enough guesses, you may eventually build something that appears to function, but that no human could ever say with certainty ever worked properly.

“With a dynamic language like Python, Perl, or Ruby, it is easy to develop software by simply banging away at the problem, often interactively, until you get what seems to be the correct result and calling it a day.
Unfortunately, this approach, while tempting, often leads to a false sense of accomplishment that is fraught with danger.Much of the danger lies in not designing a solution to be testable, and part lies in not properly controlling the complexity of the software written. If you don’t have tests, you don’t know if your software works!! PERIOD”

Unit tests framework for python – pyunit

  • Pyunit : A unit testing framework for python like JUnit for java
  • unittest supports test automation, sharing of setup and shutdown code for tests, and independence of the tests from the reporting framework. The unittest module provides classes that make it easy to support these qualities for a set of tests.
  • All your pyunit test class should extend the unittest.TestCase :
  • It supports some important concepts like:
    • setup()
    • teardown()
    • setupUpClass()
    • tearDownClass()

setup():

  • This is called immediately before calling the test method.
  • It will be called each time the test_method is called.
  • Exists to make sure that each method has its own environment to work on with and hence the each test_methods can be executed independently.
  • Any exception raised by this method will be considered an error rather than a test failure

teardown():

  • This is called immediately after the test method has been called.
  • It will be called each time the test_method is called.
  • Exists to make sure that after a test method has been executed the environment is destroyed so the next method to be executed can have a clean environment to work on with.
  • Any exception raised by this method will be considered an error rather than a test failure.
  • This method will only be called if the setUp() succeeds, regardless of the outcome of the test method.

setUpClass():

  • This is called before tests in an individual class run.
  • setUpClass is called with the class as the only argument and must be decorated as a classmethod():

    @classmethod

    def setUpClass(cls):

tearDownClass():

  • This is called after tests in an individual class run.
  • setUpClass is called with the class as the only argument and must be decorated as a classmethod():

    @classmethod

    def tearDownClass(cls):

A Simple Illustration


# A class performing basic maths
class Do_Mathematics:
def __init__(self, a, b):
self.a = a
self.b = b
def sum(self):
return self.a+self.b
def sub(self):
return self.a-self.b


#A simple test class to test the DoMathematics class
class Test_do_maths_simple(unittest.TestCase):
def setUp(self):
self.do_maths = Do_Mathematics(10, 20)
def tearDown(self):
del self.do_maths
def test_sum(self):
result = self.do_maths.sum()
self.assertEqual(result, 30, "Sum method is incorrect")
def test_sub(self):
result = self.do_maths.sub()
self.assertEqual(result, -10, "Sub method is incorrect")

What if the method which I am testing calls other methods inside?

  • Mocking the method is a solution to that.
  • Mocking a method means you are faking the call and returning a desired value for the method being mocked without executing the method.
  • The method to be mocked should exists in real, you can’t mock an imaginary method or class.
  • You can check if the mocked method is actually put into use or not by putting a VerifyAll call. If the mocked method is not being used VerifyAll will let the test to fail.

By mocking am I comprising with the quality?

  • Absolutely not.
  • You mock a method to make sure a piece of source code you are testing is sane. If the piece of code is dependent on some other piece of code you should write different unit tests for checking their sanity.


'''
Created on Apr 25, 2013
@author: abhsriva
'''
class Do_Mathematics:
def __init__(self, a, b):
self.a = a
self.b = b
def sum(self):
return self.a+self.b
def sub(self):
return self.a-self.b
def check_greater(self):
if self.a > self.b:
print "a is greater"
else:
print "b is greater"
def _check_if_zero(self, num):
if num == 0:
return True
return False
def div(self):
if self._check_if_zero(self.b):
print 'Can\'t divide'
else:
return float(self.a)/self.b

view raw

do_maths.py

hosted with ❤ by GitHub


'''
Created on Apr 25, 2013
@author: abhsriva
'''
import unittest
import mox
from class_to_be_tested import Do_Mathematics
class Test_Do_Mathematics_class(unittest.TestCase):
def setUp(self):
print 'In setup', self.x
self.mock = mox.Mox()
self.do_maths = Do_Mathematics(10, 20)
def tearDown(self):
print 'In tear down', self.x
del self.do_maths
def test_sum(self):
result = self.do_maths.sum()
self.assertEqual(result, 30, "Sum method is incorrect")
def test_sub(self):
result = self.do_maths.sub()
self.assertEqual(result, -10, "Sub method is incorrect")
def test_div(self):
self.mock.StubOutWithMock(Do_Mathematics, '_check_if_zero')
self.do_maths._check_if_zero(mox.IgnoreArg()).AndReturn(False)
self.mock.ReplayAll()
result = self.do_maths.div()
self.mock.VerifyAll()
self.assertEqual(result, 0.5, "Div method is incorrect")
def test_check_greater_with_zero(self):
self.assertTrue(self.do_maths._check_if_zero(0))
def test_check_greater_non_zero(self):
self.assertFalse(self.do_maths._check_if_zero(10))
if __name__ == "__main__":
unittest.main()