UnitTest模块

unittest组成包括以下4个核心部分:

  • test case 测试用例
    A test case is the individual unit of testing. It checks for a specific response to a particular set of inputs. unittest provides a base class, TestCase, which may be used to create new test cases.
  • test fixture 测试固件
    A test fixture represents the preparation needed to perform one or more tests, and any associate cleanup actions. This may involve, for example, creating temporary or proxy databases, directories, or starting a server process.
  • test suite 测试套件
    A test suite is a collection of test cases, test suites, or both. It is used to aggregate tests that should be executed together.
  • test runner 测试运行器
    A test runner is a component which orchestrates the execution of tests and provides the outcome to the user. The runner may use a graphical interface, a textual interface, or return a special value to indicate the results of executing the tests.

1.测试用例–继承unittest.TestCase

  • 组织形式:
class 测试类名(unittest.Test):
    #执行测试用例前的准备操作,每个测试用例执行前都会被执行
    def setUp(self):
        pass

    #测试方法名需要以test开头
    def test测试用例1(self):
        pass
    #执行完测试用例后的清理操作,每个测试用例执行完都会被执行
    def tearDown(self):
        pass
  • 代码示例:
#被测试代码
class Calc(object):
    def add(self, x, y):
        # 加法计算
        result = x + y
        return result

    def sub(self, x, y):
        # 减法计算
        result = x - y
        return result

    @classmethod
    def mul(cls, x, y):
        # 乘法计算
        result = x * y
        return result
        
		
if __name__=="__main__":
    c=Calc()
    print (c.add(1,2))
    print (c.sub(5,2))

测试代码:
import unittest
import random
import Calc
class CalcTest(unittest.TestCase):
    def setUp(self):
        #生成被测试类的实例,存在实例变量中
        self.cal = Calc.Calc()
        print ("setup completed!")

    def test_add(self):
        result = self.cal.add(1,2)
        self.assertTrue(result==3)
        #assert 3==result
               
    def test_sub(self):
        result = self.cal.sub(5,1)
        self.assertTrue(result==4)

    def tearDown(self):
        print ("tearDown completed")

if __name__ == '__main__':
    unittest.main()

2.跳过测试用例

  • 组织形式:
    @unittest.skip(reason) #无条件跳过该测试用例
    Unconditionally skip the decorated test. reason should describe why the test is being skipped.
    @unittest.skipIf(condition, reason)#满足指定条件跳过该测试用例
    Skip the decorated test if condition is true.
    @unittest.skipUnless(condition, reason)#满足指定条件执行该测试用例
    Skip the decorated test unless condition is true.

  • 代码示例:

import unittest
import random
import Calc
class CalcTest(unittest.TestCase):
    a = 1
    
    def setUp(self):
        #生成被测试类的实例,存在实例变量中
        self.cal = Calc.Calc()
        print ("setup completed!")
        
    @unittest.skip("skipping") # 无条件忽略该测试方法
    def test_add(self):
        result = self.cal.add(1,2)
        self.assertTrue(result==3)
        #assert 3==result
     
    # 如果变量a > 5,则忽略该测试方法
    @unittest.skipIf(a > 5, "condition is not satisfied!")     
    def test_sub(self):
        result = self.cal.sub(5,1)
        self.assertTrue(result==4)

    # 除非执行测试用例的平台是Linux平台,否则忽略该测试方法  win32是windows
    @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
    def test_mul(cls):
        result = self.cal.mul(2,3)
        self.assertTrue(result==6)
     
    def tearDown(self):
        print ("tearDown completed")

if __name__ == '__main__':
    unittest.main()

3.测试套件

  • 常用方法:
    addTest(test)
    Add a TestCase or TestSuite to the suite.
    addTests(tests)
    Add all the tests from an iterable of TestCase and TestSuite instances to this test suite.This is equivalent to iterating over tests, calling addTest() for each element.
  • 组织形式1:
import unittest
from Calc import Calc
class TestCalc(unittest.TestCase):
    代码略
def suite():
    #生成测试套件实例
    calcTestCase = unittest.TestSuite()
    #使用实例的addTest方法,将测试类中TestCalc的测试方法test_add加入到测试套件中
    calcTestCase.addTest(CalcTest("test_add"))
    calcTestCase.addTest(CalcTest("test_sub"))
    return calcTestCase    
  • 组织形式2:
import unittest
from Calc import Calc
class CalcTestOne(unittest.TestCase):
    代码略
class CalcTestTwo(unittest.TestCase):
    代码略
class CalcTestThree(unittest.TestCase):
    代码略
def suite():
    # 根据给定的测试类,获取其中的所有以“test”开头的测试方法,并返回一个测试套件
    suite1 = unittest.TestLoader().loadTestsFromTestCase(CalcTestOne)
    suite2 = unittest.TestLoader().loadTestsFromTestCase(CalcTestTwo)
    suite3 = unittest.TestLoader().loadTestsFromTestCase(CalcTestThree)
    # 将多个测试类加载到测试套件中
    #通过调整suit2和suite1的顺序,可以设定执行顺序
    suite = unittest.TestSuite([suite2, suite1,suite3]) 
    return suite  

4.运行测试用例

  • 方式一
    使用unitttest的main方法,直接加载测试类中的所有测试方法并执行
import unittest
from Calc import Calc
class CalcTestOne(unittest.TestCase):
    代码略
class CalcTestTwo(unittest.TestCase):
    代码略
def suite():
    代码略

if __name__=="__main__":
    unittest.main()
  • 方式二
    使用TestRunner类,执行指定的用例
import unittest
from Calc import Ca
class CalcTestOne(unittest.TestCase):
    代码略
class CalcTestTwo(unittest.TestCase):
    代码略
def suite():
    代码略

if __name__=="__main__":
    runner = unittest.TextTestRunner()
    runner.run(suite())

5.断言

  • 常用断言
Method Checks that
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNot(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not isinstance(a, b)
assertMultiLineEqual(a, b) strings
assertSequenceEqual(a, b) sequences
assertListEqual(a, b) lists
assertTupleEqual(a, b) tuples
assertSetEqual(a, b) sets or frozensets
assertDictEqual(a, b) dicts
assertRaises(exc, fun, *args, **kwds) fun(*args, **kwds) raises exc
assertRaisesRegex(exc, r, fun, *args, **kwds) fun(*args, **kwds) raises exc and the message matches regex r
assertWarns(warn, fun, *args, **kwds) fun(*args, **kwds) raises warn
assertWarnsRegex(warn, r, fun, *args, **kwds) fun(*args, **kwds) raises warn and the message matches regex r
assertLogs(logger, level) The with block logs on logger with minimum level
assertAlmostEqual(a, b) round(a-b, 7) == 0
assertNotAlmostEqual(a, b) round(a-b, 7) != 0
assertGreater(a, b) a > b
assertGreaterEqual(a, b) a >= b
assertLess(a, b) a < b
assertLessEqual(a, b) a <= b
assertRegex(s, r) r.search(s)
assertNotRegex(s, r) not r.search(s)
assertCountEqual(a, b) a and b have the same elements in the same number, regardless of their order.
  • 代码示例
class MyTest(unittest.TestCase):

    # assertEqual()方法实例
    def test_assertEqual(self):
        # 断言两数之和的结果
        try:
            a, b = 1, 2
            sum = 3
            self.assertEqual(a + b, sum, '断言失败,%s + %s != %s' %(a, b, sum))
        except AssertionError as e:
            print (e)

    # assertNotEqual()方法实例
    def test_assertNotEqual(self):
        # 断言两数之差的结果
        try:
            a, b = 5, 2
            res = 1
            self.assertNotEqual(a - b, res, '断言失败,%s - %s != %s' %(a, b, res))
        except AssertionError as e:
            print (e)

    # assertTrue()方法实例
    def test_assertTrue(self):
        # 断言表达式的为真
        try:
            self.assertTrue(1 == 1, "表达式为假")
        except AssertionError as e:
            print (e)

    # assertFalse()方法实例
    def test_assertFalse(self):
        # 断言表达式为假
        try:
            self.assertFalse(3 == 2, "表达式为真")
        except AssertionError as e:
            print (e)

    # assertIs()方法实例
    def test_assertIs(self):
        # 断言两变量类型属于同一对象
        try:
            a = 12
            b = a
            self.assertIs(a, b, "%s与%s不属于同一对象" %(a, b))
        except AssertionError as e:
            print (e)

    # test_assertIsNot()方法实例
    def test_assertIsNot(self):
        # 断言两变量类型不属于同一对象
        try:
            a = 12
            b = "test"
            self.assertIsNot(a, b, "%s与%s属于同一对象" %(a, b))
        except AssertionError as e:
            print (e)

    # assertIsNone()方法实例
    def test_assertIsNone(self):
        # 断言表达式结果为None
        try:
            result = MyClass.retrun_None()
            self.assertIsNone(result, "not is None")
        except AssertionError as e:
            print (e)

    # assertIsNotNone()方法实例
    def test_assertIsNotNone(self):
        # 断言表达式结果不为None
        try:
            result = MyClass.sum(2, 5)
            self.assertIsNotNone(result, "is None")
        except AssertionError as e:
            print (e)

    # assertIn()方法实例
    def test_assertIn(self):
        # 断言对象A是否包含在对象B中
        try:
            strA = "this is a test"
            strB = "is"
            self.assertIn(strB, strA, "%s不包含在%s中" %(strB, strA))
        except AssertionError as e:
            print (e)

    # assertNotIn()方法实例
    def test_assertNotIn(self):
        # 断言对象A不包含在对象B中
        try:
            strA = "this is a test"
            strB = "Selenium"
            self.assertNotIn(strB, strA, "%s包含在%s中" %(strB, strA))
        except AssertionError as e:
            print (e)

    # assertIsInstance()方法实例
    def test_assertIsInstance(self):
        # 测试对象A的类型是否值指定的类型
        try:
            x = MyClass
            y = object
            self.assertIsInstance(x, y, "%s的类型不是%s" %(x, y))
        except AssertionError as e:
            print (e)

    # assertNotIsInstance()方法实例
    def test_assertNotIsInstance(self):
        # 测试对象A的类型不是指定的类型
        try:
            a = 123
            b = str
            self.assertNotIsInstance(a, b, "%s的类型是%s" %(a, b))
        except AssertionError as e:
            print (e)

    # assertRaises()方法实例
    def test_assertRaises(self):
        # 测试抛出的指定的异常类型
        # assertRaises(exception)
        with self.assertRaises(TypeError) as cm:
            random.sample([1,2,3,4,5], "j")
        # 打印详细的异常信息
        #print "===", cm.exception

        # assertRaises(exception, callable, *args, **kwds)
        try:
            self.assertRaises(ZeroDivisionError, MyClass.div, 3, 0)
        except ZeroDivisionError as e:
            print (e)

    # assertRaisesRegexp()方法实例
    def test_assertRaisesRegexp(self):
        # 测试抛出的指定异常类型,并用正则表达式具体验证
        # assertRaisesRegexp(exception, regexp)
        with  self.assertRaisesRegex(ValueError, 'literal') as ar:
            int("xyz")
        # 打印详细的异常信息
        #print ar.exception
        # 打印正则表达式
        #print "re:",ar.expected_regexp

        # assertRaisesRegexp(exception, regexp, callable, *args, **kwds)
        try:
            self.assertRaisesRegexp(ValueError, "invalid literal for.*XYZ'$", int, 'XYZ')
        except AssertionError as e:
            print (e)


if __name__ == '__main__':
    # 执行单元测试
    unittest.main()

6.生成测试报告

  • 事前准备
    HTMLTestRunner扩展模块无法通过pip安装,下载完成后将HTMLTestRunner.py文件放到Python安装目录的lib文件下方可使用

  • 代码示例

import unittest
from Calc import Ca
class CalcTestOne(unittest.TestCase):
   代码略
class CalcTestTwo(unittest.TestCase):
   代码略

if __name__=="__main__":
   suite1 = unittest.TestLoader().loadTestsFromTestCase(CalcTestOne)
   suite2 = unittest.TestLoader().loadTestsFromTestCase(CalcTestTwo)
   suite = unittest.TestSuite([suite1, suite2])
   #unittest.TextTestRunner(verbosity=2).run(suite)
   filename = "e:\\pyScripts\\test.html"  # 定义个报告存放路径,支持相对路径
   # 以二进制方式打开文件,准备写
   fp = open(filename, 'wb')
   # 使用HTMLTestRunner配置参数,输出报告路径、报告标题、描述,均可以配
   runner = HTMLTestRunner.HTMLTestRunner(stream = fp,
       title = u'测试报告', description = u'测试报告内容')
   # 运行测试集合
   runner.run(suite)

7.执行顺序
setUpModule()
setUpClass()
setUp()
tearDown()
tearDownClass()
tearDownModule()

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

@classmethod
def setUpClass(cls):
    ...

tearDownClass()
A class method called after tests in an individual class have run. tearDownClass is called with the class as the only argument and must be decorated as a classmethod():

@classmethod
def tearDownClass(cls):
    ...
import unittest

class Test(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls._connection = createExpensiveConnectionObject()

    @classmethod
    def tearDownClass(cls):
        cls._connection.destroy()

你可能感兴趣的:(Python3基础教程)