预习笔记01-unittest测试框架

1. 单元测试概述

通常用于判断某个特定条件(或场景)下某个特定函数的行为。

2. 单元测试框架

  • Unittest – Python内置的标准类库。其API与JAVA的JUnit类似

  • Pytest – 更加丰富、灵活,语法简单。可以结合Allure生成酷炫的测试报告

  • Nose–对unittest的扩展

  • Mock

3. 常用的单元测试覆盖类型

ex,如下被测代码片段:

def demo_method(a,b,x):

    if (a > 1 and b == 0):
        x = x / a

    if (a == 2 or x > 1):
        x = x + 1
        
    return x
  • 语句覆盖:保证被测方法的每一行代码都被执行一遍

    1. 用例条数:1条

    2. 用例:a=3,b=0,x=4

    3. 漏洞:and 写为 or的情况,无法识别出

    4. 特点:未考虑内部逻辑,最基础、最薄弱。完全依赖行覆盖会出现严重问题

  • 判断覆盖:使得每一个判定获得每一种可能的结果

    1. 用例条数:4条(判定语句个数×单条语句可能结果数)

    2. image

    3. 漏洞:a==2 or x>1 写为 a==2 or x<1 的情况,无法识别出

    4. 特点:仅判断整个判断语句的结果,未考虑其内部的逻辑条件组合,会遗漏

  • 条件覆盖:使得每一个表达式获得每一种可能的结果

    1. 用例条数:16条(表达式个数×单条语句可能结果数)

    2. image

    3. 特点:用例指数级增长

  • 路径覆盖:覆盖所有可能执行的路径

4. unittest框架

4.1 unittest组件(官网 https://docs.python.org/3/library/unittest.html

  • test fixture - 测试装置。完成测试前准备、测试后清理操作

  • test case - 测试用例

  • test suite - 测试套件

  • test runner - 运行器。执行测试和提供结果

4.2 unittest编写规范

  • 测试模块首先 import unittest

  • 测试类必须继承 unittest.TestCase

  • 测试方法必须以 test 开头

Basic example:

import unittest


class TestStringMethods(unittest.TestCase):

    def test01_upper(self):
        print("111")
        self.assertEqual('foo'.upper(), 'FOO')
        # assert 'foo'.upper() == 'FOO'

    def test02_isupper(self):
        print("222")
        self.assertTrue('FOO'.isupper())
        self.assertFalse('Foo'.isupper())

    def test03_split(self):
        print("333")
        s = 'hello world'
        #以空格为分隔符,分隔成3个单词
        # print(s.split(' ',2)) 
        self.assertEqual(s.split(), ['hello', 'world'])
        # check that s.split fails when the separator is not a string
        with self.assertRaises(TypeError):
            #所传分隔符需要为str格式,不能是int,否则会抛出TypeError异常
            s.split(2)

if __name__ == '__main__':
    unittest.main()
  • setUp() - 测试环境准备,在每一个测试方法前运行
  • tearDown() - 测试环境清理,在每一个测试方法后运行
  • setUpClass() - 在当前测试类的所有测试方法执行前运行
  • tearDownClass() - 在当前测试类的所有测试方法执行后运行

4.3 unittest常用断言&&用例执行方式

  • 方式一:执行当前文件所有的unittest测试用例,全部执行

  • 方式二:执行指定的测试用例 - 将要执行的测试用例添加到测试套件中,批量执行测试方法

  • 方式三:执行指定的测试类 - 将测试类添加到测试套件里面, 批量执行测试类

  • 方式四:匹配某个目录下所有满足unittest规则的测试用例,执行这些py文件下的所有测试用例

#1、setUp、tearDown、setUpClass、tearDownClass方法练习
#2、unittest常用断言的使用
#3、执行测试用例方法
import unittest


#被测试方法
class Search():

    def search_func(self):
        print("search")
        return True

class TestSearch(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print('setupclass')
        cls.search = Search()

    @classmethod
    def tearDownClass(cls):
        print('teardownclass')

    def test_search1(self):
        print("testsearch1")
        # search = Search()
        assert True == self.search.search_func()

    def test_search2(self):
        print("testsearch2")
        # search = Search()
        assert True == self.search.search_func()

    def test_search3(self):
        print("testsearch3")
        # search = Search()
        assert True == self.search.search_func()

class TestSearch1(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print('setupclass1')
        cls.search = Search()

    @classmethod
    def tearDownClass(cls):
        print('teardownclass1--1')

    def test_search1(self):
        print("testsearch1--1")
        # search = Search()
        assert True == self.search.search_func()

    def test_search2(self):
        print("testsearch2--1")
        # search = Search()
        assert True == self.search.search_func()

    def test_search3(self):
        print("testsearch3--1")
        # search = Search()
        assert True == self.search.search_func()

    def test00(self):
        self.assertIsInstance(self.search,Search,"search is for Search")
        self.assertNotIsInstance(2, Search,"2 is not for Search")


if __name__ == '__main__':
    #方式一:执行当前文件所有的unittest测试用例,全部执行
    unittest.main()

    #方式二:执行指定的测试用例,将要执行的测试用例添加到测试套件里面,批量执行测试方法
    #创建一个测试套件,testsuite
    suite = unittest.TestSuite()
    #将测试用例添加到测试套件中
    suite.addTest(TestSearch1("test00"))
    suite.addTest(TestSearch1("test_search3"))
    #使用unittest.TextTestRunner()方法执行suite套件
    unittest.TextTestRunner().run(suite)

    #方式三:执行某个测试类,将测试类添加到测试套件里面, 批量执行测试类
    #将测试类添加到测试套件中
    suite1 = unittest.TestLoader().loadTestsFromTestCase(TestSearch)
    suite2 = unittest.TestLoader().loadTestsFromTestCase(TestSearch1)
    #创建一个测试套件
    suite = unittest.TestSuite([suite1,suite2])
    unittest.TextTestRunner(verbosity=2).run(suite)
    #方式二和方式三都需要创建测试套,区别在于添加用例的方式不同,一个是用addTest方法,添加测试类中的测试用例;一个是是用TestLoader添加测试类
    #将要执行的用例全部添加到测试套件suite后,就可以运行套件从而执行用例


#方式四:匹配某个目录下所有满足unittest规则的测试用例,执行这些py文件下的所有测试用例
import unittest

#入口方法
if __name__ == '__main__':
    #在当前路径下找到要执行的模块路径
    test_dir = "./testcases"
    #将要执行的所有用例添加到discover
    discover = unittest.defaultTestLoader.discover(test_dir,pattern="test*.py")
    unittest.TextTestRunner().run(discover)

4.4 unittest 结合 htmltestrunner 生成测试报告

import unittest
from util.HTMLTestRunner_PY3 import HTMLTestRunner

if __name__ == '__main__':
    report_title = 'Example用例执行报告'
    desc = '用于展示修改样式后的HTMLTestRunner'
    report_file = './result.html'

    #在当前路径下找到要执行的发现路径
    test_dir = "./testcases"
    #将要执行的所有用例添加到discover
    discover = unittest.defaultTestLoader.discover(test_dir,pattern="test*.py")
    with open(report_file, 'wb') as report:
        runner = HTMLTestRunner(stream=report, title=report_title, description=desc)
        runner.run(discover)

在浏览器查看测试报告 result.html :