Unittest测试框架(重点在了解思想)

单元测试概述

什么是单元测试(白盒测试)?
单元测试是开发者编写的一小段代码,用于检验被测代的一个很小的、很明确的功能是否正确.。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。
单元测试什么时候开始?
越早介入越好
单元测试由谁负责?
由开发负责

单元测试需要注意

单元测试的时候一个大前提就是需要清楚的知道,自己要测试的程序块所预期的输入和输出,然后根据这个预期和程序逻辑来书写case。这里的预期结果一定要针对需求/设计的逻辑去写,而不是针对程序的实现去写,否则单测就失去了意义,照着错误的实现设计出的case也很可能是错的。

单元测试框架列举及国内大厂现状

Unittest
Python内置的标准类库。它的API跟Java的JUnit、.net的NUnit,C++的CppUnit很相似
Pytest
丰富、灵活的测试框架,语法简单,可以结合allure生成一个炫酷的测试报告,现在比较主流
Nose
Nose是对unittest的扩展,使得python的测试更加简单
Mock
unittest.mock是用来测试python的库。这个是一个标准库(出现在python3.3版本以后)

单元测试覆盖率

代码覆盖率也被用于自动化测试和手工测试来度量测试是否全面的指标之一,应用覆盖率的思想增强测试用例的设计
单元测试覆盖类型:
语句覆盖
条件覆盖
判断覆盖
路径覆盖
被测试代码片段

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

在这里插入图片描述

语句覆盖

语句覆盖定义

通过设计一定量的测试用例,保证被测试的方法每一行代码都会被执行一遍
运行测试用例的时候被击中的代码行即称为被覆盖的语句
测试用例

仅需要一条case,即可实现行覆盖
a =3, b=0, x=4
漏洞

and -> or
行覆盖是一个最基础的覆盖方式,但是也是最薄弱的,如果完全依赖行覆盖,会出现很严重的问题

判断覆盖

判断覆盖定义
运行测试用例的过程中被击中的判定语句
测试用例

在这里插入图片描述

漏洞
大部分的判定语句是由多个逻辑条件组合而成,若仅仅判断其整个最终结果,而忽略每个条件的取值情况,必然会遗漏部分测试路径
a2 or x>1 -> a2 or x<1
条件覆盖

条件覆盖定义

条件覆盖和判定覆盖类似,不过判定覆盖关注整个判定语句,而条件覆盖则关注某个判断条件
测试用例:if(a>1 and b ==0)

在这里插入图片描述

缺陷
测试用例指数级增加(2**conditions)

路径覆盖

路径覆盖定义
覆盖所有可能执行的路径
测试用例

在这里插入图片描述
在这里插入图片描述

应用这些方法设计测试用例

unittest框架介绍

官网: https://docs.python.org/3/library/unittest.html#unittest.TextTestRunner
python自带的单元测试框架,常用在单元测试
在自动化测试中提供用例组织与执行
提供丰富的断言方法-验证函数等功能
加上HTMLTestRunner可以生成html的报告

unittest编写与规范

Unittest提供了了test cases、test suites、test fixtures、test runner相关的组件
编写规范
测试模块首先import unittest
测试类必须继承unittest.TestCase
测试方法必须以"test_"开头
类名使用“驼峰”命名法

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()

测试框架结构

总结:
setUp用来为测试准备环境, tearDown用来清理环境
如果想要在所有case执行之前准备一次环境,并在所有case执行结束之后再清理环境,我们可以用setUpClass() 与 tearDownClass();比如:数据库连接及销毁
如果想有些方法不在本次执行使用@unittest.skip
测试方法的命名:以test开头
各种执行-单一用例,全部

class TestClass(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:
        print("这是测试整个类前要执行的方法")
    
    def setUp(self) -> None:
        print("这是每一个测试方法前面运行的方法")
        
    def tearDown(self) -> None:
        print("这是每一个测试方法后面运行的方法")
    
    @unittest.skip("这次不想执行这个测试")
    def test_second(self):
        print("这是测试方法2")
        
    @classmethod
    def tearDownClass(cls) -> None:
        print("这是测试整个类后要执行的方法")

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

Tips:-> None 表示默认返回值为 None。setUp 和 tearDown 是在每条测试用例的前后调用,setUpClass 和 tearDownClass 是在整个测试类的前后调用,注意 setUpClass 和 tearDownClass 要加上装饰器@classmethod。unittest.main() 可以调用当前模块所有的测试类和测试用例。

setUp 与 tearDown

基于测试方法级别的setUp,tearDown
执行每个测试方法的时候都会执行一次setUp和tearDown
基于类级别的setUpClass,tearDownClass
执行这个类里面的所有测试方法只有一次执行setup, tearDown。

unittest断言

用unittest组件测试用例的时候,断言的方法还是很多的,下面介绍几种常用的断言方法: assertEqual、assertln、assertTrue

基本断言方法

基本的断言方法提供了测试结果是True还是False。所有的断言方法都有一个msg参数,如果指定msg参数的值,则将该信息作为失败的错误信息返回。

在这里插入图片描述

Tips:Pycharm中运行测试用例后程序会自动记住上一次的执行代码,这时候需要去右上角点击项目将运行历史清空。
在这里插入图片描述

unittest执行测试用例

编写unittest测试用例的原则

unittest会自动识别以test开头的函数是测试代码,test一定要是小写
执行测试用例的顺序

测试用例执行顺序是以test后面的字母顺序执行的。例如test_a,test_b,test_c
多个测试用例的集合就是测试套件,通过测试套件来管理多个测试用例

执行方法一:

unittest.main()

执行方法二:加入容器中执行

suite=unittest.TestSuite()
suite.addTest(TestMethod("test_01"))
suite.addTest(TestMethod("test_02"'))
unittest.TextTestRunner().run(suite)

执行方法三:此用法可以同时测试多个类

suite1 = unittest.TestLoader().loadTestsFromTestCase(TestCase1)
suite2 = unittest.TestLoader().loadTestsFromTestCase(TestCase2)
suite = unittest.TestSuite([suite1, suite2])
unittest.TextTestRunner(verbosity=2).run(suite)

执行方法四:匹配某个目录下所有以test开头的py文件,执行这些文件下的所有测试用例

	test_dir = "./ test_case"
	
	discover = unittest.defaultTestLoader.discover(test_dir, pattern="test*.py")

discover可以一次调用多个脚本
test_dir 被测试脚本的路径
pattern脚本名称匹配规则

import unittest
class Search():
    def search(self):
        print("search")

class TestSearch(unittest.TestCase):
    def setUp(self) -> None:
        print("setUp_方法级别")
        self.search = Search()

    def tearDown(self) -> None:
        print("tearDown_方法级别")
        self.search = Search()

    @classmethod
    def setUpClass(cls) -> None:
        print("setUp_类级别")
        cls.search = Search()

    @classmethod
    def tearDownClass(cls) -> None:
        print("tearDown_类级别")
        cls.search = Search()

    def test_search1(self):
        print("search1")
        assert True == self.search.search()

    def test_equal(self):
        print("断言相等")
        self.assertEqual(1,2,"判断1==2")

    def test_noteqal(self):
        print("断言不相等")
        self.assertNotEqual(1,2,"判断1!=2")

class TestSearch1(unittest.TestCase):
    def setUp(self) -> None:
        print("setUp_方法级别")
        self.search = Search()

    def tearDown(self) -> None:
        print("tearDown_方法级别")
        self.search = Search()

    @classmethod
    def setUpClass(cls) -> None:
        print("setUp_类级别")
        cls.search = Search()

    @classmethod
    def tearDownClass(cls) -> None:
        print("tearDown_类级别")
        cls.search = Search()

    def test_search1(self):
        print("search1")
        assert True == self.search.search()

    def test_equal(self):
        print("断言相等")
        self.assertEqual(1,2,"判断1==2")

    def test_noteqal(self):
        print("断言不相等")
        self.assertNotEqual(1,2,"判断1!=2")

if __name__ == '__main__':
# 方法一:运行所用测试用例
# unittest.main()

# 方法二:测试套件
# 创建一个测试套件,testsuite
suite = unittest.TestSuite()
# 只运行TestSearch1下的test_search1测试用例
suite.addTest(TestSearch1("test_search1"))
# 执行用例
unittest.TextTestRunner().run(suite)

# 方法三:执行某个测试类
suite1 = unittest.TestLoader().loadTestsFromTestCase(TestSearch1)
suite2 = unittest.TestLoader().loadTestsFromTestCase(TestSearch)
suite = unittest.TestSuite([suite1, suite2])
unittest.TextTestRunner(verbosity=2).run(suite)

Tips:Pycharm运行时IDE不太准确,可以在Pycharm中切换到命令行里面运行,如:python test_search.py

D:\0_NSFocus_My_Files\5_Python_Test\Unittest_Demo>python test_unittest.py
setUp_类级别
setUp_方法级别
search1
search
tearDown_方法级别
FtearDown_类级别

======================================================================
FAIL: test_search1 (__main__.TestSearch1)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_unittest.py", line 63, in test_search1
    assert True == self.search.search()
AssertionError

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)

执行方法四:
目录结构:

在这里插入图片描述

import unittest

if __name__ == '__main__':
    test_dir = './testcases'
    discover = unittest.defaultTestLoader.discover(test_dir, pattern="test*.py")
    unittest.TextTestRunner(verbosity=2).run(discover)

测试用例执行过程
在这里插入图片描述

unittest实战

Unittest 安装
unittest 使用示例
unittest 结合 htmltestrunner 生成带日志的测试报告
http://tungwaiyip.info/software/HTMLTestRunner.html (python2版本)
https://github.com/huilansame/HTMLTestRunner_PY3 (python3版本)
import unittest

from Unittest_Demo.HTMLTestRunner import HTMLTestRunner

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

    test_dir = './testcases'
    discover = unittest.defaultTestLoader.discover(test_dir, pattern="test*.py")
    unittest.TextTestRunner(verbosity=2).run(discover)

    with open(report_file,'wb') as report:
        runner = HTMLTestRunner(stream=report, title=report_titile,description=desc)
        runner.run(discover)

共勉:【可能给予你助力的教程】

这些资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。
在这里插入图片描述
我的学习交流群::644956177
在这里插入图片描述

关注我的微信公众号【程序媛木子】免费获取~

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值