• unittest单元测试框架


    单元测试负责对最小的软件设计单元进行验证,它使用软件设计文档对模块的描述作为指南,对重要的程序分支进行测试以发现模块中的错误。

    1.1如何使用unittest单元测试框架

     1 calculator.py
     2 
     3 # -*- coding:utf-8 -*-
     4 
     5 class Count:
     6     def __init__(self , a , b):
     7         self.a = a
     8         self.b = b
     9     def add(self):
    10         return self.a + self.b
    11 
    12 test.py
    13 
    14 
    15 import unittest
    16 from calculator import Count
    17 
    18 
    19 class TestCount(unittest.TestCase):
    20     def setUp(self):
    21         print("test start")
    22 
    23     def tearDown(self):
    24         print("test stop")
    25 
    26     def test_add(self):
    27         count = Count(3, 4)
    28         self.assertEqual(count.add(), 7)
    29 
    30 
    31 if __name__ == '__main__':
    32     unittest.main()

    引入 unittest 模块。如果想用unittest 编写测试用例,那么一定要遵守它的“规则”。

    • (1)创建一个测试类,这里为TestCalculator 类,必须要继承unittest 模块的TestCase类。
    • (2)创建一个测试方法,该方法必须以“test”开头。

    unittest提供了全局的main()方法,main方法使用TestLoader类来搜索所有包含在该模块中以“test”命名开头的测试方法,并且自动执行他们。

    一些概念:

    • 1).Test Case:测试用例,就是一个完整的测试流程,包括环境的搭建,测试,环境的还原等,通过这个测试用例,可以对某个功能进行验证。
    • 2).Test Suite:测试套件,用来组装多个测试用例。unittest提供了TestSuite 类来创建测试套件。
    • 3).Test Runner:在unittest中,提供了TextTestRunner类运行测试用例,为了生成HTML 格式的测试报告,后面会选择使用HTMLTestRunner 运行类。
    • 4).Test Fixture:对一个测试用例环境的搭建和销毁就是Test Fixture,通过覆盖测试用例的setUp和tearDown来实现。
     1 
     2 import unittest
     3 from calculator import Count
     4 
     5 
     6 class Test(unittest.TestCase):
     7 
     8     def setUp(self):
     9         print("test begin")
    10     def tearDown(self):
    11         print("test end")
    12     def test_add2(self):
    13         count = Count(4, 13)
    14         self.assertEqual(count.add(), 17)
    15 
    16     def test_add(self):
    17         count = Count(2, 4)
    18         self.assertEqual(count.add(), 6)
    19 
    20 if __name__ == '__main__':
    21     suite = unittest.TestSuite()
    22     suite.addTest(Test("test_add"))
    23     runner = unittest.TextTestRunner()
    24     runner.run(suite)
    25 
    26 -------输出结果-------
    27 PS E:Python> python3 test.py
    28 test begin
    29 test end
    30 .
    31 ----------------------------------------------------------------------
    32 Ran 1 test in 0.000s
    33 
    34 OK

    说明:如上首先使用unittest的TestSuite类来创建测试套件,然后通过addTest(),添加测试用例,最后通过unittest的TextTestRunner类的run方法运行测试套件。

    1.2unittest提供的断言方法

    unittest的TestCase类提供了许多对测试结果的判断,如下:

    方法 检查 版本 说明
    assertEqual(a, b) == b   判断第一个参数和第二个参数是否相等,不相等则测试失败
    assertNotEqual(a, b) != b   判断第一个参数和第二个参数是否不相等,相等则测试失败
    assertTrue(x) bool(x) is True   判读参数x是否为true,为false则测试失败
    assertFalse(x) bool(x) is False   判断参数x是否为false,为true则测试失败
    assertIs(a, b) is b 3.1   判断第一个参数和第二个参数是否为同一对象,不是则测试失败
    assertIsNot(a, b) is not b 3.1   判断第一个参数和第二个参数是否不为同一个对象,是则测试失败
    assertIsNone(x) is None 3.1   判断x是否为None,不是则测试失败
    assertIsNotNone(x) is not None 3.1   判断x是否不为None,是则测试失败
    assertIn(a, b) in b 3.1   判断a是否在b中,不在则测试失败
    assertNotIn(a, b) not in b 3.1   判断a是否不在b中,在则测试失败
    assertIsInstance(a, b) isinstance(a, b) 3.2   判断a是否为b的一个实例,不是则测试失败
    assertNotIsInstance(a, b) not isinstance(a, b) 3.2   判断a是否不为b的一个实例,是则测试失败

    1.3discover组织测试用例

    当测试用例达到成百上千条时,组织测试用例时十分麻烦,unittest模块的TestLoader类提供了discover方法可以解决这样的问题。

    TestLoader类根据各种标准加载测试用例,并将它们返回给测试套件,不需要创建TestLoader类的实例,unittest提供了defaultTestLoader类可以使用其子类和方法创建TestLoader实例,其中discover就是其中的一个。

    1 discover(self, start_dir, pattern='test*.py', top_level_dir=None)
    2 start_dir:需要测试的模块名或测试用例目录。
    3 pattern:用例文件名的匹配原则。
    4 top_level_dir:测试模块的顶层目录。

     1 calculator.py
     2 
     3 # -*- coding:utf-8 -*-
     4 
     5 class Count:
     6     def __init__(self, a, b):
     7         self.a = a
     8         self.b = b
     9 
    10     # 加法
    11     def add(self):
    12         return self.a + self.b
    13 
    14     # 减法
    15     def sub(self):
    16         return self.a - self.b
    17 
    18 
    19 import unittest
    20 from calculator import Count
    21 
    22 testadd.py
    23 
    24 class TestAdd(unittest.TestCase):
    25 
    26     def setUp(self):
    27         print("test start")
    28 
    29     def tearDown(self):
    30         print("test end")
    31 
    32     def test_add(self):
    33         count = Count(3, 5)
    34         self.assertEqual(count.add(), 8)
    35 
    36     def test_add2(self):
    37         count = Count(5, 8)
    38         self.assertEqual(count.add(), 13)
    39 
    40 
    41 if __name__ == '__main__':
    42     unittest.main()
    43 
    44 testsub.py
    45 
    46 
    47 import unittest
    48 from calculator import Count
    49 
    50 class TestSub(unittest.TestCase):
    51 
    52     def setUp(self):
    53         print("test start")
    54 
    55     def tearDown(self):
    56         print("test end")
    57 
    58     def test_sub(self):
    59         count = Count(7, 3)
    60         self.assertEqual(count.sub(), 4)
    61 
    62     def test_sub2(self):
    63         count = Count(8, 2)
    64         self.assertEqual(count.sub(), 6)
    65 
    66 
    67 if __name__ == '__main__':
    68     unittest.main()
    69 
    70 runtest.py
    71 
    72 
    73 import unittest
    74 
    75 test_dir = "./"
    76 discover = unittest.defaultTestLoader.discover(test_dir, pattern="test*.py")
    77 
    78 if __name__ == '__main__':
    79     runner = unittest.TextTestRunner()
    80     runner.run(discover)

    说明:discover会在test_dir下查找test*.py,然后将测试用例放到测试套件中,所以可以通过run方法运行。

    1.4用例执行的顺序

    默认情况下,unittest根据ASCII码的顺序加载测试用例:0~9,A~Z,a~z,因此TestA类会优先于TestB,TestB会优先于Testc,同样,对于测试目录和测试文件来说,也是这样来加载测试用例。

    如果想要改变unittest的默认加载测试用例的顺序,可以使用unittest框架的TestSuite类的addTest方法改变测试用例的优先级。

    我们第3讲中的discover方法加载测试用例的方法也是按照ASCII码的顺序加载测试用例的,所以如果我们想要提高测试用例的优先级,只能通过测试用例的命名了,如test_a优先于test_b。

    执行多级目录的测试用例

    当测试用例的数量达到一定量级时,就要考虑目录划分,比如规划如下测试目录。
    test_project
    ├──/test_case/
    │ ├── test_bbb/
    │ │ ├── test_ccc/
    │ │ │ └── test_c.py
    │ │ └── test_b.py
    │ ├── test_ddd/
    │ │ └── test_d.py
    │ └── test_a.py
    └─ run_tests.py

    对于上面的目录结构,如果将discover()方法中的start_dir 参数定义为“./test_case”目录,那么只能加载test_a.py 文件中的测试用例。如何让unittest 查找test_case/下子目录中的测试文件呢?方法很简单,就是在每个子目录下放一个__init__.py 文件。__init__.py 文件的作用是将一个目录标记成一个标准的Python 模块。

    1.5跳过测试和预期失败

    unittest框架提供了一些装饰器,使得我们可以把不想要执行的测试用例直接跳过,或者是当某一条件成立时跳过或执行测试用例,也或者把某一个用例设置为失败,不论该用例是否执行成功。

     1 
     2 import unittest
     3 
     4 
     5 class Test(unittest.TestCase):
     6 
     7     def setUp(self):
     8         print("test begin")
     9 
    10     def tearDown(self):
    11         print("test end")
    12 
    13     @unittest.skip("不运行该测试用例,直接跳过")
    14     def test_skip(self):
    15         pass
    16 
    17     @unittest.skipIf(1>2, "条件成立时,跳过该测试用例")
    18     def test_if_skip(self):
    19         pass
    20 
    21     @unittest.skipUnless(1>2, "条件成立时,执行该测试用例")
    22     def test_unless(self):
    23         pass
    24 
    25     @unittest.expectedFailure  # 不管执行结果是否失败,都会标记为失败,但不会抛出异常信息
    26     def test_expected_failure(self):
    27         pass
    28 
    29 
    30 if __name__ == '__main__':
    31     unittest.main()

    说明:上面的装饰器也可以用在类上面。

    1.6组织Web测试用例

    目录结构:

    文件中代码如下:

     1 test_baidu.py
     2 
     3 # coding:utf-8
     4 import unittest
     5 from selenium import webdriver
     6 import time
     7 
     8 
     9 class TestBaidu(unittest.TestCase):
    10 
    11     def setUp(self):
    12         self.driver = webdriver.Firefox()
    13         self.driver.maximize_window()
    14         self.driver.implicitly_wait(10)
    15         self.base_url = "http://www.baidu.com"
    16 
    17     def tearDown(self):
    18         self.driver.quit()
    19 
    20     def test_baidu(self):
    21         driver = self.driver
    22         driver.get(self.base_url)
    23         driver.find_element_by_id("kw").send_keys("selenium")
    24         driver.find_element_by_id("su").click()
    25         time.sleep(3)
    26         self.assertEqual(driver.title, "selenium_百度搜索")
    27 
    28 
    29 if __name__ == '__main__':
    30     unittest.main()
    31 
    32 test_sogou.py
    33 
    34 # coding:utf-8
    35 import unittest
    36 from selenium import webdriver
    37 import time
    38 
    39 
    40 class TestSouGou(unittest.TestCase):
    41     def setUp(self):
    42         self.driver = webdriver.Firefox()
    43         self.base_url = "http://www.sogou.com"
    44         self.driver.maximize_window()
    45         self.driver.implicitly_wait(5)
    46 
    47     def tearDown(self):
    48         self.driver.close()
    49 
    50     def test_sogou(self):
    51         driver = self.driver
    52         driver.get(self.base_url)
    53         driver.find_element_by_id("query").clear()
    54         driver.find_element_by_id("query").send_keys("selenium")
    55         driver.find_element_by_id("stb").click()
    56         time.sleep(2)
    57         self.assertEqual(driver.title, "selenium - 搜狗搜索")
    58 
    59 
    60 if __name__ == '__main__':
    61     unittest.main()
    62 
    63 runtest.py
    64 
    65 # coding:utf-8
    66 import unittest
    67 
    68 discover = unittest.defaultTestLoader.discover("./testpro/testcase/", "test*.py")
    69 
    70 if __name__ == '__main__':
    71     runner = unittest.TextTestRunner()
    72     runner.run(discover)

    在cmd中切换到testpro文件上一目录,然后执行如下命令:

    1 python3 runtest.py >>./testpro/testresult/log.txt 2>&1

    打开log.txt文件,查看其中内容:

    1 ..
    2 ----------------------------------------------------------------------
    3 Ran 2 tests in 66.344s
    4 
    5 OK
  • 相关阅读:
    go语言xrom使用
    go语言算法
    go语言递归
    go语言map(字典)
    GO语言实现小技巧
    偶遇递归树
    Python中字典对象根据字典某一个key和values去重
    python中将字符串格式转字典
    Azure媒体服务的Apple FairPlay流功能正式上线
    SVG裁剪和平移的顺序
  • 原文地址:https://www.cnblogs.com/zhuzhaoli/p/10493358.html
Copyright © 2020-2023  润新知