While browsing through python and discovering new facets of python everyday, I found about unit testing feature in python. As the name suggests "Unit testing" is basically testing of code which are part of a larger module. It is nothing but a sort of white box testing where we are only bothered about whether the code gives a correct output rather than the more complex case that is how it was implemented. The advantages of using unit testing techniques is that we are able to identify the errors more easily, the fixing is simpler and most important it is less complex.
Now when i talk about python and how it supports unit testing, there are 3 techniques that i came across. I would like to give you a gist of these techniques how you can incorporate the same while working on python projects.
The 3 techniques are- Unit test, doctest, nose test
Now when i talk about python and how it supports unit testing, there are 3 techniques that i came across. I would like to give you a gist of these techniques how you can incorporate the same while working on python projects.
The 3 techniques are- Unit test, doctest, nose test
Why Unit test?
Unit test is a module available for all python 2.1 and above. By importing this module in your program. The basic idea is to think of all the positive and negative inputs your program might face while its use. This way you start with all such scenarios even before actual building of code. An example for this I came across while browsing was with roman numerals. Roman numerals are meant only for counting purposes so only real life numbers are interpreted in roman numerals. So all negatives, fractions, zero find no place in roman numerals. Also they are limited to the range of 1--3999. So again anything beyond 3999 is a myth in roman.
Now when we import unittest module we define the situations which the code will face in its test cases. We define a dictionary for every number till 3999.
- Test for success- We then decide positive test cases where the program should run successfully and reach the desired outcome. Eg: For any number in the range of 1-3999.
- Test for failure- We then decide negative test cases where the program should throw an error as it doesn't satisfy the critiera. Eg: A number greater than 3999 or a decimal number, 0 etc.
- Test for sanity- We test if we convert a number to roman numeral then on converting back we should obtain the number itself and not lose it to roundoffs etc. Eg: tonumber(toroman(n)) = n should give n itself.
Why DocTest?
Doctest is an interactive python to check whether the output given is according to the desired output or not. The ways to use doctest are:-
- To check that a module’s docstrings are up-to-date by verifying that all interactive examples still work as documented.
- To perform regression testing by verifying that interactive examples from a test file or a test object work as expected.
- To write tutorial documentation for a package, liberally illustrated with input-output examples. Depending on whether the examples or the expository text are emphasized, this has the flavor of “literate testing” or “executable documentation”
def factorial(n:)
import math if not n >= 0: raise ValueError("n must be >= 0") if math.floor(n) != n: raise ValueError("n must be exact integer") if n+1 == n: # catch a value like 1e300 raise OverflowError("n too large") result = 1 factor = 2 while factor <= n: result *= factor factor += 1 return result if __name__ == "__main__": import doctest doctest.testmod()
Now a common error, many of us may get an error that doctest doesn't have the file testmode in it. It is because we created a py file doctest.py which on running creates a file doctest.pyc. Remove the .pyc file, rename your file and try again, your file will run absolutely fine.
Another feature is testfile(). Here it searches in any given .txt file, if it contains any python code in it and runs test cases for it and returns error message if any errors, or checks if its correct.
syntax-
import doctest doctest.testfile("example.txt")
Doctest also provides many options which you can find in the doctest module like ignoring blankspaces, ellipses(...),dont accept true value flags, normalize whitespaces.
Doctest integrates tester class which has testsuites for integrating different testcases for different modules for it BASIC API.
The advanced API has a framework as given in the figure.
list of:
+------+ +---------+
|module| --DocTestFinder-> | DocTest | --DocTestRunner-> results
+------+ | ^ +---------+ | ^ (printed)
| | | Example | | |
v | | ... | v |
DocTestParser | Example | OutputChecker
+---------+
It also provides objects like finder objects, parser objects, runner objects and output checker objects. It also provides debugger options which can be run under the python debugger pdb.
Why nose tool?
For nose tools testing you need to have nose installed in your systems, which can be easily done using apt - get install. Writing testcases is easier as nose collects cases from unittest.TestCase.
Running testcases is easier as nose automatically collects as long as you follow some simple
guidelines for organizing your library and test code. There’s no need
to manually collect test cases into test suites. Running tests is
responsive, since nose begins running tests as soon as the first test
module is loaded. Nose supports fixtures at the package, module, class, and test case
level, so expensive initialization can be done as infrequently as
possible. It comes with builtin plugins to help in easier output capture, error finding, code coverage etc.
The code shown below is an example code I tried for a python game. It gives a unit test skeleton for nose tools.
from nose.tools import * from ex47.game import Room def test_room(): gold = Room("GoldRoom", """This room has gold in it you can grab. There's a door to the north.""") assert_equal(gold.name, "GoldRoom") assert_equal(gold.paths, {}) def test_room_paths(): center = Room("Center", "Test room in the center.") north = Room("North", "Test room in the north.") south = Room("South", "Test room in the south.") center.add_paths({'north': north, 'south': south}) assert_equal(center.go('north'), north) assert_equal(center.go('south'), south)
On simply running nosetests on your terminal, you get the following output log:-
~/projects/simplegame $ nosetests ... ---------------------------------------------------------------------- Ran 3 tests in 0.007s OK
Now that we have more than one way to test our code with little more ease, all we need to do now is start right away thinking of test cases for a code still in pipeline. All I can say for now is HAPPY TESTING to all!!!
No comments:
Post a Comment