Thursday, December 6, 2018

Testing Python code 2

We've used assertEqual() in the previous post which is one of the assert methods in Python's unittest.TestCase class. We know that assert methods test whether a condition you believe is true at a specific point in your code is indeed true. If the condition is true as expected, your assumption about how that part of your program behaves is confirmed; you can be confident that no errors exist. If the condition you assume is true is actually not true, Python raises an exception. Before moving to class testing let's discuss assertions first.

Assertions

An assertion is a sanity check to make sure your code isn’t doing something obviously wrong. These sanity checks are performed by assert statements. If the sanity check fails, then an AssertionError exception is raised. In code, an assert statement consists of the following:

• The assert keyword
• A condition (that is, an expression that evaluates to True or False)
• A comma
• A string to display when the condition is False

I am using the example used in my previous post to demonstrate how assertion works. Also if you can recall the question I asked regarding the output of running the last code snippet in the post, the answer you'll find now. See the program shown below:

import unittest

from display_name import display_cname

class CnameTestCase(unittest.TestCase):
   
    def test_first_last_name(self):
       
        complete_name = display_cname('veevaeck','swami')
        self.assertEqual(complete_name,'Veevaeck Swami')
       
    def test_first_last_middle_name(self):
       
        complete_name = display_cname('veevaeck','kumar','swami')
        self.assertEqual(complete_name,'Veevaeck Kumar Swami')
       
unittest.main()


The code for display_cname() is  as follows:

def display_cname(fname,lname,mname=''):
   
    if mname:
       
        cname = fname +' '+ mname +' '+ lname
       
    else:
       
        cname = fname +' '+ lname
       
           
    return cname.title() 



Now run test_display_name.py file. You should get the output as shown below:



Notice the Assertion error reported:

AssertionError: 'Veevaeck Swami Kumar' != 'Veevaeck Kumar Swami'

Now review the code for display_cname() and the code in test_display_name.py program where the display_cname() is called.

def display_cname(fname,lname,mname='')
complete_name = display_cname('veevaeck','kumar','swami')

Did you catch the fault? 

The assertion catches this mistake and clearly tells us what’s wrong. In plain English, an assert statement says, “I assert that this condition holds true, and if not, there is a bug somewhere in the program.”

The display_cname() function has the parameter mname (for middle name) as the third argument whereas in the test_display_name.py program where the display_cname() is called the middle name was passed as the second parameter. Hence Python raised the following exception:

AssertionError: 'Veevaeck Swami Kumar' != 'Veevaeck Kumar Swami'

Just change the argument order while calling the display_cname() as shown below and run the program, the test should pass.

complete_name = display_cname('veevaeck','swami','kumar')

The output with the corrected code should be:

..
----------------------------------
Ran 2 tests in 0.001s

OK


------------------
(program exited with code: 0)

Press any key to continue . . . 


Unlike exceptions, your code should not handle assert statements with try and except; if an assert fails, your program should crash. By failing fast like this, you shorten the time between the original cause of the bug and when you first notice the bug. This will reduce the amount of code you will have to check before finding the code that’s causing the bug.

Assertions are for programmer errors, not user errors. For errors that can be recovered from (such as a file not being found or the user entering invalid data), raise an exception instead of detecting it with an assert statement.

The six commonly used assert methods are:

assertEqual(a, b)        Verify that a == b
assertNotEqual(a, b)  Verify that a != b
assertTrue(x)              Verify that x is True
assertFalse(x)             Verify that x is False
assertIn(item, list)       Verify that item is in list
assertNotIn(item, list) Verify that item is not in list

With these methods we can verify that returned values equal or don’t equal expected values, that values are True or False, and that values are in or not in a given list. We can use these methods only in a class that inherits from unittest .TestCase, so let’s look at how we can use one of these methods in the context of testing an actual class.

Disabling Assertions

Assertions can be disabled by passing the -O option when running Python. This is good for when you have finished writing and testing your program and don’t want it to be slowed down by performing sanity checks (although most of the time assert statements do not cause a noticeable speed difference).

Assertions are for development, not the final product. By the time you hand off your program to someone else to run, it should be free of bugs and not require the sanity checks.
 
Testing a Class

Testing a class is similar to testing a function—much of your work involves testing the behavior of the methods in the class. But there are a few differences, so let’s write a class to test. Consider a class that checks whether appropriate complete name of a user is displayed or not. Yes! you are right! I am using the same logic which I used to demonstrate testing of functions. See the code for class to be tested:

class Cname():
   
    def display_cname(self,fname,lname,mname=''):
       
        if mname:
       
            cname = fname +' '+ mname +' '+ lname
       
        else:
       
            cname = fname +' '+ lname
       
           
        return cname.title()


   
The class has only one method display_cname which accepts parameters from a user and depending on the entered parameters prints the complete name of the user. To show that the Cname class works, let’s write a program that uses the class:

from cname import Cname

first_name = input("Enter your first name: ")

last_name = input("\nEnter your last name: ")

question = input("\nDo you have a middle name(Y/N): ")

if question == 'Y':

    middle_name = input("\nEnter your middle name: ")
   
else:
   
    middle_name =''

cn =Cname()

complete_name = cn.display_cname(first_name,last_name,middle_name)

print("\nYour complete name is " + complete_name)


This program prompts a user to enter his first name, last name and the middle name if the user has one. Then we create an object of Cname class and using this object call display_cname() function which returns the complete name of the user. We store this complete name in the variable complete_name and finally print the information in this variable. When we run this program the following output is seen:






If the user has a middle name then:



Let’s write a test that verifies one aspect of the way Cname class behaves. We'll name our test class as CnameTestCase which will be stored as test_Cname.py for which the code is shown below:

import unittest

from cname import Cname

class CnameTestCase(unittest.TestCase):
   
    def test_first_last_name(self):
        fl = Cname()
        complete_name = fl.display_cname('veevaeck','swami')
        self.assertEqual(complete_name,'Veevaeck Swami')
       
    def test_first_last_middle_name(self):
        flm = Cname()
        complete_name = flm.display_cname('veevaeck','swami','kumar')
        self.assertEqual(complete_name,'Veevaeck Kumar Swami')
       
unittest.main()


The class CnameTestCase inherits from unittest.TestCase and has two methods to test complete names. The test_first_last_name() method if a user has only first and last name and test_first_last_middle_name() method if a user has first name, last name and middle name. In both the methods we instantiate the Cname class and create an object using which we call the display_cname() function. Finally using the assertEqual() method we test if the input value matches the output values.

When we run the test_Cname.py program following output is shown:

..
--------------------------------
Ran 2 tests in 0.001s

OK


------------------
(program exited with code: 0)

Press any key to continue . . .

This output indicates that our test for the behavior of Cname class is passed which means our class will produce the desired output.

If you can see in the test_Cname.py program, in both the methods we instantiate the Cname class and create an object using which we call the display_cname() function. The unittest.TestCase class has a setUp() method that allows you to create these objects once and then use them in each of your test methods. When you include a setUp() method in a TestCase class, Python runs the setUp() method before running each method starting with test_. Any objects created in the setUp() method are then available in each test method you write. Let's revise our code to include this setUp() method:

import unittest

from cname import Cname

class CnameTestCase(unittest.TestCase):
   
    def setUp(self):
       
        self.fl = Cname()
   
    def test_first_last_name(self):
       
        complete_name = self.fl.display_cname('veevaeck','swami')
        self.assertEqual(complete_name,'Veevaeck Swami')
       
    def test_first_last_middle_name(self):
       
        complete_name = self.fl.display_cname('veevaeck','swami','kumar')
        self.assertEqual(complete_name,'Veevaeck Kumar Swami')
       
unittest.main()


As you can see we have created an object fl in the setUp() which is prefixed by self, so it can be used anywhere in the class. This makes the two test methods simpler, because neither one has to make a Cname instance. When we run the test_Cname.py program now again both the tests should pass. When you’re testing your own classes, the setUp() method can make your test methods easier to write. You make one set of instances and attributes in setUp() and then use these instances in all your test methods. This is much easier than making a new set of instances and attributes in each test method.

Here we end today's discussion and in the next post we'll continue with the remaining topics within the testing of Python. So till we meet next keep practicing and learning Python as Python is easy to learn!











 














Share:

0 comments:

Post a Comment