Test
General Rules
  • A testing unit should focus on one tiny bit of functionality and prove it correct
  • Each test unit must be fully independent
  • Try hard to make tests that run fast
  • Learn your tools and learn how to run a single test or a test case
  • Always run the full test suite before a coding session, and run it again after
  • Runs all tests before pushing code to a shared repository
  • Use long and descriptive names for testing functions
  • Test Outcomes
  • ok, the test passes
  • FAIL, the test does not pass, and raises an AssertionError exception
  • ERROR, the test raises an exception other than AssertionError
  • Layout and pytest

    setup.py
    from setuptools import setup
    
    setup(
        name='ECSUProject_2',
        version='0.1',
        description='Demo for building a Python project',
        author='Lin Chen',
        author_email='lchen@ecsu.edu',
        url='http://lin-chen-va.github.io',
        install_requires=["numpy >= 1.10", "biopython >= 1.10"],
        packages=['myFormat', ],
        package_dir={'':'src'},
        setup_requires=["pytest-runner"],
        tests_require=["pytest"],
        scripts=['src/convert',],
        license='Creative Commons Attribution-Noncommercial-Share Alike license',
        long_description=open('README').read(),
        classifiers=[
          'Development Status :: 4 - Beta',
          'Environment :: X11 Applications :: GTK',
          'Intended Audience :: End Users/Desktop',
          'Intended Audience :: Developers',
          'License :: OSI Approved :: GNU General Public License (GPL)',
          'Operating System :: POSIX :: Linux',
          'Programming Language :: Python',
          'Topic :: Desktop Environment',
          'Topic :: Text Processing :: Fonts'
          ],
    )
    		
    setup.cfg
    [aliases]
    test=pytest
    		
    MANIFEST.in
    include README LICENSE
    recursive-include doc/_build/html *
    		
    convert
    #!/usr/bin/python
    
    import sys
    from myFormat.m1 import convertRead, convertWrite
    from myFormat.m2 import upper
    
    def main():
        if len(sys.argv) != 3:
            print 'python convert.py [inputFile] [outputFile]'
            exit()
    
        try:
            lines_of_file = convertRead(sys.argv[1])  # read file
            upper_lines = upper(lines_of_file)  # convert strings to upper case
            convertWrite(upper_lines, sys.argv[2])  # save to output file
        except Exception, err:
            print err
    
    if __name__ == '__main__':
        print 'file: ', __file__
        main()
    		
    myFormat/m1.py
    #!/usr/bin/python
    
    def convertRead(inputfile):
        l = [];
        try:
            with open(inputfile, 'rb') as f:
                for line in f:
                    l.append(line);
        except Exception, err:
            raise IOError("Errors in reading "+inputfile)
        return l;
    
    def convertWrite(l, outputfile):
        try:
            with open(outputfile, 'wb') as f:
                for line in l:
                    f.write(line);
        except Exception, err:
            raise IOError("Errors in writing "+outputfile)
    		
    myFormat/m2.py
    #!/usr/bin/python
    
    def upper(l):
        temp = [];
        for s in l:
            temp.append(s.upper());
        return temp;
    
    def lower(l):
        temp = [];
        for s in l:
            temp.append(s.lower());
        return temp;
    		
  • Use pytest instead of default test, the default test may ignore some exceptions
  • test
  • search for test_*.py or *_test.py files, imported by their test package name, collect test_ prefixed test functions or methods as test items
  • test does not install dependencies to system, it install the copies of dependencies to .eggs in the project folder
  • test files must have unique names, the test files will be imported as top-level modules by adding test/ to sys.path
  • assert
    test/test_m1.py
    #!/usr/bin/python
    
    from myFormat.m1 import convertRead, convertWrite
    
    def test_convertRead():
    
        try:
            l = convertRead('test/test_file.txt')
        except Exception, err:
            raise IOError("test file test_file.txt is not accessible")
    
        assert l[0] == 'Hello\n'
        assert l[1] == 'World!\n'
    
    def test_convertWrite():
    
        l = ["Hello\n", "World!\n"]
        try:
            convertWrite(l, 'test/test_output.txt')
        except Exception, error:
            raise IOError("Not able to write output to a file")
    
        try:
            l2 = convertRead('test/test_output.txt')
        except Exception, err:
            raise IOError("test/test_output.txt does not exist")
    
        assert l[0] == 'Hello\n'
        assert l[1] == 'World!\n'
    		
    test/test_m2.py
    #!/usr/bin/python
    
    from myFormat.m2 import upper, lower
    
    def test_upper():
        l = ['Lower Case', '!@$#$']
        l2 = upper(l)
    
        assert l2[0] == 'LOWER CASE'
        assert l2[1] == '!@$#$'
    
    def test_lower():
        l = ['Lower Case', '!@#$%']
        l2 = lower(l)
    
        assert l2[0] == 'lower case'
        assert l2[1] == '!@#$%'
    		
    test/test_convert.py
    #!/usr/bin/python
    
    from myFormat.m1 import convertRead
    
    def test_convert():
        import os
        os.system('python src/convert.py test/test_file.txt test/test_output2.txt')
    
        try:
            l = convertRead('test/test_output2.txt')
        except Exception, err:
            raise IOError('test/test_output2.txt is not accessible')
    
        assert l[0] == 'HELLO\n'
        assert l[1] == 'WORLD!\n'
    		
    Unittest
    test/test_m1.py
    #!/usr/bin/python
    
    import unittest
    from myFormat.m1 import convertRead, convertWrite
    
    class TestM1(unittest.TestCase):
        def test_convertRead(self):
            try:
                l = convertRead('test/test_file.txt')
            except Exception, err:
                raise IOError("test file test_file.txt is not accessible")
    
            self.assertEqual(l[0], 'Hello\n')
            self.assertEqual(l[1], 'World!\n')
    
        def test_convertWrite(self):
            l = ["Hello\n", "World!\n"]
            try:
                convertWrite(l, 'test/test_output.txt')
            except Exception, error:
                raise IOError("Not able to write output to a file")
    
            try:
                l2 = convertRead('test/test_output.txt')
            except Exception, err:
                raise IOError("test/test_output.txt does not exist")
    
            self.assertEqual(l[0], 'Hello\n')
            self.assertEqual(l[1], 'World!\n')
    
    if __name__ == '__main__':
        unittest.main()
    		
  • failUnless() | failIf() | failUnlessEqual() | failIfEqual() | failUnlessRaises() | fail()
  • assertTrue() | asserFalse() | assertEqual() | assertRaises() | id()
  • import unittest
    
    class TestStringMethods(unittest.TestCase):
        def test_upper(self):
            self.assertEqual('foo'.upper(), 'FOO')
    
        def test_isupper(self):
            self.assertTrue('FOO'.isupper())
            self.assertFalse('Foo'.isupper())
    
        def test_split(self):
            s = 'hello world'
            self.assertEqual(s.split(), ['hello', 'world'])
            # check that s.split fails when the separator is not a string
            with self.assertRaises(TypeError):
                s.split(2)
                                                                                        
    if __name__ == '__main__':
        unittest.main()
    		
    #!/usr/bin/python
    
    import unittest
    
    def raiseException():
        raise ValueError('Invalid value ...');
    
    class SimplisticTest(unittest.TestCase):
    
        def setUp(self):
            self.fixture = range(1, 10);
            print 'In setUp ...';
    
        def tearDown(self):
            del self.fixture;
            print 'In tearDown ...';
    
        def test(self):
            self.failUnless(True);
    
        def testFail(self):
            self.failIf(False, 'Test fail ...');
    
        def testError(self):
            raise RuntimeError('Test error ...');
    
        def testAssertTrue(self):
            self.assertTrue(True, 'Test assert true ...');
    
        def testAssertFalse(self):
            self.assertFalse(False);
    
        def testEqual(self):
            self.failUnlessEqual(1, 3-2);
    
        def testRaiseError(self):
            self.failUnlessRaises(ValueError, raiseException);
    
    class secondTest(unittest.TestCase):
    
        def testSecond(self):
            print 'test second ...';
    
    if __name__ == '__main__':
        suite = unittest.TestLoader().loadTestsFromTestCase(SimplisticTest);
        suite_2 = unittest.TestLoader().loadTestsFromTestCase(secondTest);
        allTests = unittest.TestSuite([suite, suite_2]);
        #unittest.TextTestRunner(verbosity=2).run(allTests);
        unittest.main();
    		
    Nose
    #!/usr/bin/python
    
    def test_numbers_3_4():
        assert 3*5 >= 12
    			
    nosetests testCode.py -v
  • setup_module() | teardown_module(), before and end of the module
  • with_setup(), before and end of the function
  • setup() | teardown(), before and end of each function in the class
  • setup_class() | teardown_class(), before and end of the class
  • Doctest
    #!/usr/bin/python
    
    def squareTest(x):
        """Return the square of x.
    
        >>> squareTest(2)
        4
        >>> squareTest(-2)
        4
        """
        return x*x;
    
    if __name__ == '__main__':
        import doctest
        doctest.testmod();
    
    			
    python testCode.py -v
    Pytest
    Python Testing
    pytest testCode.py -v
    python -m pytest testCode.py -v
    Reference
  • nose introduction
  • Python unittest Fixtures
  • unittest documentation
  • unittest at PYMOTW
  • Good Integration Practices
  • Hitchhiker’s Guide