cse 399-004: python programmingcse39904/lectures/07-packagecmdunit.pdfthe unittest module: overview...
TRANSCRIPT
CSE 399-004:Python Programming
Lecture 07:Packages, Command-line arguments, and Unit testing
February 26, 2007
http://www.seas.upenn.edu/~cse39904/
Announcements
• No homework this week• There may be one or two more homeworks later• If so, they will be short
• Projects• Should have received feedback on your proposal• Might be a good idea to start on them now• Some of you were asked to send me email• I'll ask later in the term if I should change the due date
• No office hours during Spring Break• I'll be in town though• Send email if you want to meet
2
Today
• Packages
• Command-line arguments
• Unit testing: The unittest module
3
Packages(Tutorial, Section 6.4)
Packages: Overview
• A package collects together many modules and organizes them into some hierarchy
• The organization of files (modules) on disk determines the structure of the hierarchy
• Useful since putting many modules in the same directory can be cumbersome
• Aside: Packages in Python are similar to packages in Java
5
Packages: Concretely
• A package in Python is simply any directory that contains a file named __init__.py
6
Sound/ __init__.py Formats/ __init__.py wavread.py wavwrite.py ... Effects/ __init__.py echo.py surround.py ...
A sample package hierarchy.
Packages: Concretely
• A package in Python is simply any directory that contains a file named __init__.py
6
Sound/ __init__.py Formats/ __init__.py wavread.py wavwrite.py ... Effects/ __init__.py echo.py surround.py ...
Unlike in Java, nothing special is needed within these files to make them part of a package.
Packages: Concretely
• A package in Python is simply any directory that contains a file named __init__.py
6
Sound/ __init__.py Formats/ __init__.py wavread.py wavwrite.py ... Effects/ __init__.py echo.py surround.py ...
A subpackage of the Sound package.
Importing from packages
• import Sound.Effects.echo• from Sound.Effects import echo
7
Sound/ __init__.py Formats/ __init__.py wavread.py wavwrite.py ... Effects/ __init__.py echo.py surround.py ...
Importing from packages
• import Sound.Effects.echo• from Sound.Effects import echo
7
Sound/ __init__.py Formats/ __init__.py wavread.py wavwrite.py ... Effects/ __init__.py echo.py surround.py ...
Only the "dot-notation" is new here.
Imports within packages
• Imports across package boundaries need to use the full package names
8
Sound/ __init__.py Formats/ __init__.py wavread.py wavwrite.py ... Effects/ __init__.py echo.py surround.py ...
Imports within packages
• Imports across package boundaries need to use the full package names
8
Sound/ __init__.py Formats/ __init__.py wavread.py wavwrite.py ... Effects/ __init__.py echo.py surround.py ...
import surroundimport Sound.Formats.wavread
echo.py
Imports within packages
• Imports across package boundaries need to use the full package names
9
Sound/ __init__.py Formats/ __init__.py wavread.py wavwrite.py ... Effects/ __init__.py echo.py surround.py ...
import surroundfrom ..Formats import wavread
echo.py
Python also supports relative imports, but I think it's clearer to write out paths in full.
Command-line arguments
Why command-line arguments?
• Even in this age of graphical user interfaces (GUIs), it is still common to invoke programs using a command-line
• It's not nice to pester the user with lots of questions as to what the program should do
• For example, the Unix cp program has something like 10–20 different options to control its behavior
• Command-line arguments are an easy way for a user to signal to a program what they want it to do
11
What are command-line arguments?
12
% cse39904submit -a hw5 html/index.html
What are command-line arguments?
12
% cse39904submit -a hw5 html/index.html
program name
What are command-line arguments?
12
% cse39904submit -a hw5 html/index.html
Three arguments:(1) -a(2) hw5(3) html/index.html
What are command-line arguments?
13
% cse39904submit -a hw5 html/index.html
Arguments of the form "-foo" are sometimes referred to as "options" or "flags".
sys.argv
• The sys module has a variable argv which is a list of the command-line arguments given to the current program
• Each argument is always a string
• The first argument is always the name of the program (e.g., file) being run
14
sys.argv: Example
15
#!/usr/bin/env pythonimport sysprint sys.argv
foo.py
% ./foo.py arg1 200 arg3['./foo.py', 'arg1', '200', 'arg3']% python foo.py a b 30['foo.py', 'a', 'b', '30']% python foo.py['foo.py']
sys.argv: Example
15
% ./foo.py arg1 200 arg3['./foo.py', 'arg1', '200', 'arg3']% python foo.py a b 30['foo.py', 'a', 'b', '30']% python foo.py['foo.py']
The "program name" is useful only your program depends on "how it was called" or if you need to generate something like a usage message.
The optparse module
• Parsing "interesting" command-line arguments by hand is painful and error prone except
• The optparse module provides an easy way for you to specify flags/options and then parse them
16
cse39904submit (simplified)
17
# Create a new options parser.optparser = optparse.OptionParser()
# Specify an option to look for.optparser.add_option("-a", dest="assn", help="the assignment to submit files for")
# Parse everything in sys.argv.options, args = optparser.parse_args()
cse39904submit (simplified)
17
# Create a new options parser.optparser = optparse.OptionParser()
# Specify an option to look for.optparser.add_option("-a", dest="assn", help="the assignment to submit files for")
# Parse everything in sys.argv.options, args = optparser.parse_args()
Automatically knows that -h means to print out a help message. Does not know about any other flags.
cse39904submit (simplified)
17
# Create a new options parser.optparser = optparse.OptionParser()
# Specify an option to look for.optparser.add_option("-a", dest="assn", help="the assignment to submit files for")
# Parse everything in sys.argv.options, args = optparser.parse_args()
Tells the parser to look for the -a flag.
cse39904submit (simplified)
18
# Create a new options parser.optparser = optparse.OptionParser()
# Specify an option to look for.optparser.add_option("-a", dest="assn", help="the assignment to submit files for")
# Parse everything in sys.argv.options, args = optparser.parse_args()
Actually parse sys.argv. Information about flags passed in is stored in options, and args will be a list of all other command-line arguments.
cse39904submit (simplified)
19
# Create a new options parser.optparser = optparse.OptionParser()
# Specify an option to look for.optparser.add_option("-a", dest="assn", help="the assignment to submit files for")
# Parse everything in sys.argv.options, args = optparser.parse_args()
% cse39904submit➞ options.assn == None➞ args == [ ]
cse39904submit (simplified)
20
# Create a new options parser.optparser = optparse.OptionParser()
# Specify an option to look for.optparser.add_option("-a", dest="assn", help="the assignment to submit files for")
# Parse everything in sys.argv.options, args = optparser.parse_args()
% cse39904submit -a hw1➞ options.assn == 'hw1'➞ args == [ ]
cse39904submit (simplified)
21
# Create a new options parser.optparser = optparse.OptionParser()
# Specify an option to look for.optparser.add_option("-a", dest="assn", help="the assignment to submit files for")
# Parse everything in sys.argv.options, args = optparser.parse_args()
% cse39904submit -a hw1 file1 file2➞ options.assn == 'hw1'➞ args == [ 'file1', 'file2']
cse39904submit (simplified)
22
# Create a new options parser.optparser = optparse.OptionParser()
# Specify an option to look for.optparser.add_option("-a", dest="assn", help="the assignment to submit files for")
# Parse everything in sys.argv.options, args = optparser.parse_args()
% cse39904submit file1 -a hw1 file2➞ options.assn == 'hw1'➞ args == [ 'file1', 'file2']
References
• There are many things you can do with OptParsers
• http://docs.python.org/lib/module-optparse.html
23
Unit testing: Overview
What is unit testing?
• Well-written programs can be broken up into "units"• Individual functions and methods• Classes• Modules• Packages
• Unit testing aims to test the functionality of all the "units" in your program
25
Why unit testing?
• Testing whole programs is difficult• Too many code paths to verify• Too many behaviors to check for• Isolating bugs is tricky
• It makes more sense to test things by checking the smaller units first, then the larger ones
• This naturally isolates bugs and problematic behavior
• Test cases themselves can serve as documentation, e.g., examples of (in)correct behavior
26
Regression testing
• Programs evolve over time• New features are added• Bugs are uncovered, then fixed
• New code can be incorrect or break existing code• Makes similar mistakes as previously written code• Breaks long held assumptions
• Regression testing involves the periodic running of test cases to monitor code for the reemergence of bugs
• Easy to do with a suite of unit tests
27
Problems with unit testing
• Many pieces of code cannot run in complete isolation• They may rely on external resources• They may rely on other code behaving correctly
• Two techniques to get around this:• Test harnesses: Create a suitable environment before
running certain test cases• Mock objects: When testing, say, one class, create
dummy instances of other necessary classes
• Goal here: To test one piece of code without relying on other pieces to behave correctly
28
Caveats about testing in general
• You need to know what your code is supposed to do before you can write test cases for it
• But, you can still write the test cases for a given piece of code before you write the code itself
• Beware of bugs in the test cases themselves
• The test cases are, after all, simply more code…
• Be very careful when using your program's output to write test cases — you don't want to use buggy output!
29
References
• http://en.wikipedia.org/wiki/Unit_testing
• http://en.wikipedia.org/wiki/Regression_testing
30
Unit testing:The unittest module
The unittest module: Overview
• The unittest module makes it relatively easy to write a suite of unit tests for your programs
• It is modeled after JUnit, a Java unit testing framework
• Aside: There are many ways to use unittest• I'll describe one way of organizing test cases• The module provides other ways
• Goal for today: Basic introduction to unittest
32
33
import randomimport unittest
class TestSequenceFunctions(unittest.TestCase): def setUp(self): self.seq = range(10)
... <methods for test cases elided here> ...
if __name__ == '__main__': unittest.main()
33
import randomimport unittest
class TestSequenceFunctions(unittest.TestCase): def setUp(self): self.seq = range(10)
... <methods for test cases elided here> ...
if __name__ == '__main__': unittest.main()
The class representing a test case.Can be used to defined multiple cases at once.
34
import randomimport unittest
class TestSequenceFunctions(unittest.TestCase): def setUp(self): self.seq = range(10)
... <methods for test cases elided here> ...
if __name__ == '__main__': unittest.main()
No need to define a constructor.
34
import randomimport unittest
class TestSequenceFunctions(unittest.TestCase): def setUp(self): self.seq = range(10)
... <methods for test cases elided here> ...
if __name__ == '__main__': unittest.main()
The setUp() method is called before each test case in this class is run. A similar method tearDown() is called after each test case is run, no matter the result.
35
import randomimport unittest
class TestSequenceFunctions(unittest.TestCase): def setUp(self): self.seq = range(10)
... <methods for test cases elided here> ...
if __name__ == '__main__': unittest.main()
Runs the test cases defined in this module.
36
class TestSequenceFunctions(unittest.TestCase):
# setUp() method from previous slide def testshuffle(self): random.shuffle(self.seq) self.seq.sort() self.assertEqual(self.seq, range(10))
def testchoice(self): element = random.choice(self.seq) self.assert_(element in self.seq)
def testsample(self): self.assertRaises(ValueError, random.sample, self.seq, 20) for element in random.sample(self.seq, 5): self.assert_(element in self.seq)
36
class TestSequenceFunctions(unittest.TestCase):
# setUp() method from previous slide def testshuffle(self): random.shuffle(self.seq) self.seq.sort() self.assertEqual(self.seq, range(10))
def testchoice(self): element = random.choice(self.seq) self.assert_(element in self.seq)
def testsample(self): self.assertRaises(ValueError, random.sample, self.seq, 20) for element in random.sample(self.seq, 5): self.assert_(element in self.seq)
Any method whose name starts with "test" defines a test case. These methods should take no arguments.
37
class TestSequenceFunctions(unittest.TestCase):
# setUp() method from previous slide def testshuffle(self): random.shuffle(self.seq) self.seq.sort() self.assertEqual(self.seq, range(10))
def testchoice(self): element = random.choice(self.seq) self.assert_(element in self.seq)
def testsample(self): self.assertRaises(ValueError, random.sample, self.seq, 20) for element in random.sample(self.seq, 5): self.assert_(element in self.seq)
setUp() is called before each method is called,so these self.seq's will all be equal to range(10).
38
class TestSequenceFunctions(unittest.TestCase):
# setUp() method from previous slide def testshuffle(self): random.shuffle(self.seq) self.seq.sort() self.assertEqual(self.seq, range(10))
def testchoice(self): element = random.choice(self.seq) self.assert_(element in self.seq)
def testsample(self): self.assertRaises(ValueError, random.sample, self.seq, 20) for element in random.sample(self.seq, 5): self.assert_(element in self.seq)
The TestCase class provides several methods which test cases should use to test whether certain conditions hold.
39
class TestSequenceFunctions(unittest.TestCase):
# setUp() method from previous slide def testshuffle(self): random.shuffle(self.seq) self.seq.sort() self.assertEqual(self.seq, range(10))
def testchoice(self): element = random.choice(self.seq) self.assert_(element in self.seq)
def testsample(self): self.assertRaises(ValueError, random.sample, self.seq, 20) for element in random.sample(self.seq, 5): self.assert_(element in self.seq)
TestCase.assertEqual() : Used to test if two values are equal.
40
class TestSequenceFunctions(unittest.TestCase):
# setUp() method from previous slide def testshuffle(self): random.shuffle(self.seq) self.seq.sort() self.assertEqual(self.seq, range(10))
def testchoice(self): element = random.choice(self.seq) self.assert_(element in self.seq)
def testsample(self): self.assertRaises(ValueError, random.sample, self.seq, 20) for element in random.sample(self.seq, 5): self.assert_(element in self.seq)
TestCase.assert_() : Used to test if something is true.
41
class TestSequenceFunctions(unittest.TestCase):
# setUp() method from previous slide def testshuffle(self): random.shuffle(self.seq) self.seq.sort() self.assertEqual(self.seq, range(10))
def testchoice(self): element = random.choice(self.seq) self.assert_(element in self.seq)
def testsample(self): self.assertRaises(ValueError, random.sample, self.seq, 20) for element in random.sample(self.seq, 5): self.assert_(element in self.seq)
TestCase.assertRaises() : Test if some exception is thrown.
Failures versus errors
• unittest distinguishes "failures" from "errors"
• Failure: The test case was expecting one thing to happen, and instead something else happen
• These happen when some assert*() method fails to detect the expected condition or event
• These correspond to incorrect answers
• Error: An unexpected exception is thrown
• Corresponds to code doing something incorrect42
More unittest terminology
• Test fixture: The framework needed to run a single test case; managed by the TestCase class
• Test case: Smallest unit of testing; managed by TestCase
• Test suite: A collection of test cases and test suites• Test suites can be built up from other suites
• Test runner: Component which actually runs test cases and test suites
43
References
• http://docs.python.org/lib/module-unittest.html
• In particular, see the TestCase class for all the various assert* and fail* methods that test cases can use
44
Final remarks
• Next class is Monday, March 12, 2007
• Do try to enjoy Spring Break
• List of (potential) future topics:• Graphical user interfaces (GUIs) [next time?]• Regular expressions• Networking• Functional programming [near end?]
• Some of you may have to figure out the above topics before I get a chance to say anything about them…
45