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

 

posted @ 2019-03-09 17:09  博小园  阅读(805)  评论(0编辑  收藏  举报
回到顶部