自动化测试——UnitTest框架

一、UnitTest框架

UnitTest是目前市场应用主流的测试框架技术之一。其实只是用来做测试用例管理以及生成测试报告的一个模块而已。Junit和UnitTest是同一个家族的成员。

UnitTest是Python的自带官方库之一,所以不需要做任何的环境安装和部署。

UnitTest4大组件:

  • 前置与后置条件:测试流程执行前必须要提前准备好的内容叫做前置条件。测试流程执行结束之后必须要执行的内容叫做后置条件。前置条件一般用于做初始化行为,后置条件一般用于做资源释放的相关操作。前后置条件不会关联到任何业务逻辑。setup与teardown是前置与后置。定义是通过函数来实现。分为函数级别和类级别的前后置。

  • 测试用例:TestCase,在UnitTest中有自己的用例管理模式,包括排序、命名规则等相关。

  • 断言机制:UnitTest封装有自己的断言函数。用于在断言时调用,所有的断言都可以通过self.的方式来进行断言的方法调用。

  • 套件与运行器:套件TestSuite是用于做测试用例的管理的模块,便于整个测试流程的执行、冒烟测试、测试抽查、持续集成等需求的应用。运行器Runner是用于生成测试结果(测试报告),通过运行套件的测试用例来生成最终的测试结果


二、 UnitTest的语法规则

UnitTest的语法规则:
        1. 创建一个UnitTest类,是通过继承的方式来实现,继承的对象固定写法是unittest.TestCase
        2. 测试用例管理,测试用例在UnitTest中是以函数的形式来实现的。且用例的名称一定要用test开头,否则无法识别为测试用例
            个人建议,所有用例命名的时候通过test_序号_名称来定义,例如def test_01_login
        3. unittest的调用,一定是在main中调用unittest.main()的方式来实现。
        4. 测试用例运行流程:
            1. 获取所有的测试用例(包含test开头的函数)
            2. 对测试用例进行排序,排序规则是固定的ascii规则进行排序。0-9,A-Z,a-z的顺序
        5. 用例在运行过程中,如果没有报错,则表示通过,如果报错,不管是否为断言报错,都会判定为是失败的测试用例。一般不会轻易
            添加try..except来进行用例内容的管理
        6. UnitTest运行时控制台信息的顺序,不要纠结。因为UnitTest本身打印信息在控制台会出现有乱序的情况。
        7. 用例的内容设计的时候,一定要尽可能减少彼此之间的关联性。每一个测试用例尽可能的独立化。更好确保其他的流程用例执行时不受到影响。
            复杂业务流程下,可以通过用多个不同的文件,来管理不同的测试流程,
            简单业务流程下,可以一个用例实现一个流程。
        8. UnitTest支持普通函数的编写。需要注意命名的时候不要用test开头
        9. UnitTest支持你进行源码的修改,修改用例排序规则,修改用例更名规则等。应用场景极少,基本不需要,做知识扩充了解即可。

'''··

下面我们对UnitTest规则进行详细介绍:

 2.1 创建一个UnitTest类

创建一个UnitTest类,是通过继承的方式来实现,继承的对象固定写法是unittest.TestCase

在创建类的同时,我们也需要导包

import unittest


# 创建UnitTest类
class UnitDemo(unittest.TestCase):

2.2 测试用例管理

 测试用例管理,测试用例在UnitTest中是以函数的形式来实现的。且用例的名称一定要用test开头,否则无法识别为测试用例。

个人建议,所有用例命名的时候通过test_序号_名称来定义,例如def test_01_login。因为对测试用例的调用是有一定顺序的:0-9,A-Z,a-z的顺序,我们可以看下面的例子:

import unittest
class UnitDemo(unittest.TestCase):
    def test_02_login(self):
        print("这是测试用例02")
    def test_01_simple(self):
        print("这是测试用例01")
   

 结果是:

可以看到测试用例执行的时候是按照一定的顺序,并不按照从上到下的顺序。

2.3测试用例的调用

def 定义的test_ 是测试用例,只有执行 if __name__ == '___mian___' 的时候会执行测试用例,其他普通函数则不执行,通过 self 来调用执行。

具体来说就是在测试用例下面加上main方法:

# unittest导包
import unittest


# 创建UnitTest类
class UnitDemo(unittest.TestCase):
    # 定义测试用例是通过def来完成
    def test_02_login(self):
        print('这是测试用例02')

    def test_01_simple(self):
        print('这是测试用例01')


# UnitTest运行的固定写法
if __name__ == '__main__':
    unittest.main()

2.4断言的使用

# 创建UnitTest类
class UnitDemo(unittest.TestCase):
    # 定义测试用例是通过def来完成
    def test_02_login(self):
        print('这是测试用例02')

    def test_01_simple(self):
        print('这是测试用例01')

    def test_03_failed(self):
        assert 1 == 2

# UnitTest运行的固定写法
if __name__ == '__main__':
    unittest.main()

assert也可以使用在测试用例中, 用例在运行过程中,如果没有报错,则表示通过,如果报错,不管是否为断言报错,都会判定为是失败的测试用例。一般不会轻易, 添加try..except来进行用例内容的管理。

import unittest
class UnitDemo(unittest.TestCase):
    def test_02_login(self):
        print("这是测试用例02")
    def test_01_simple(self):
        print("这是测试用例01")
    def test_03_falied(self):
        assert 1 == 2
    def test_04_exception(self):
        try:
            print(1/0)
        except:
            print("hei,报错了")
    def login5(self):
        print("这是login")
#unitTest运行的固定写法
if __name__ == '__main__':
    unittest.main()

比如在第四个测试用例里面,通过异常处理机制,纵然1/0会报错。但是由于try..except捕获异常,因此这个用例也会显示通过,看一下以上四个测试用例的执行结果。4条只有用例3没通过。

 2.5UnitTest中的普通函数

UnitTest支持普通函数的编写。需要注意命名的时候不要用test开头。

 普通函数,在UnitTest中不会被直接运行,有需要的时候通过调用函数来执行。

def test_05(self):
        self.login()

    # 普通函数,在UnitTest中不会被直接运行,有需要的时候通过调用函数来执行
    def login(self):
        print('这是login')

我们可以看到是可以被调用的 


三、UnitTest前后置 

 前置与后置条件:
        Unittest中前后置是固定写法,都是setupteardown
        1. 前后置只能够定义一个
        2. 前后置级别是函数级和类级。
        3. 函数级前后置条件,在每一个测试用例执行前后都会分别执行一次。
        4. 类级别的前后置条件,在每一个类运行前后分别执行一次。

        5. 不要在前后置中进行逻辑代码的编写,所有的初始化和资源释放,是不需要有逻辑的

3.1函数级setup和teardown 

import unittest


class TestSimple(unittest.TestCase):
    # 函数级
    # 前置条件的定义
    def setUp(self):
        print('这是setup')

    # 后置条件
    def tearDown(self) -> None:
        print('这是teardown')

当我们去运行以上写的5条测试用例

mport unittest


class TestSimple(unittest.TestCase):
    # 函数级
    # 前置条件的定义
    def setUp(self):
        print('这是setup')

    # 后置条件
    def tearDown(self) -> None:
        print('这是teardown')
def test_02_login(self):
        print('这是测试用例02')

    def test_01_simple(self):
        print('这是测试用例01')

    def test_03_failed(self):
        assert 1 == 2

    def test_04_exception(self):
        try:
            print(1 / 0)
        except:
            print('嘿! 我报错啦~')

    def test_05(self):
        self.login()

 加了函数级的用例执行:在每一个测试用例执行前后都会分别执行一次。如果有多个setup,只会按照第一个setup。

3.2类级别setup和teardown 

# 类级别
    @classmethod
    def setUpClass(cls) -> None:
        print('这是类级别的setup')
        cls.a = 'hcc'

    @classmethod
    def tearDownClass(cls) -> None:
        print('这是类级别的teardown')

 我们加载之后发现报错了

 在定义类级别的setup时,我们一定要加@classmethod装饰器,证明他是类级别的

 

 类级别的前后置条件,只会在整个类前后运行一次。


四、断言的使用

在UnitTest中,提供有非常多的自定义断言内容,直接通过self.的方式就可以直接调用。其中,所定义的这些断言机制,已经覆盖有非常非常多的内容了。所以在进行自动化测试时,我们可以直接通过调用自带的断言来实现用例的通过与否的判断。

 那些以assert开头的方法都是可以用作断言的方法:

 4.1断言常见方法

  • assertEqual() # 判断两者是否相等
  • assertTrue()   # 判断目标是否为真
  • assertIn()       # 判断是否包含
  • self.assertIs() #判断是否是

具体使用:

assertEqual(参数1,参数2,msg) 方法的使用:参数1等于参数2断言成功,参数1不等于参数2断言失败,发送msg

class UnitDemo(unittest.TestCase):
    def test_01(self):
        self.assertEqual(1,2,msg='断言失败')#判断两者是否相等

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

assertTrue(参数1,msg)方法的使用:参数1为真,断言成功;参数1不为真,发送msg

    def test_02(self):
        self.assertTrue(False,msg='断言失败')#判断目标是否为真

  assertIn(参数1,参数2,msg)  方法的使用:参数1在参数2断言成功,参数1在参数2断言失败,发送msg

 def test_03(self):
        self.assertIn('a','abc',msg='断言成功')

 assertIn(参数1,参数2,msg)方法的使用:参数1是参数2类型的,断言成功,参数1不是参数2类型的,断言失败,发送msg

    def test_04(self):
        self.assertIs(type(1),int,msg='断言呦呦呦失败了')


五、测试套件与运行器

5.1测试套件与运行器的使用

在UnitTest中,创建测试套件,必须要在新的py文件中进行创建,在原有UnitTest文件中创建,运行时无效。 

测数套件:
        是用于专门管理UnitTest中所有测试用例的类。相当于是文件系统中的文件夹。而测试用例则是一个个的文件。
        套件在实际使用中,可以不遵循UnitTest的用例排序规则,遵循的规则是添加顺序的规则。
        测试套件,是通过unittest.TestSuite类来实现

运行器:
        所有的测试套件,都是基于运行器来执行的,所以有套件情况下,必须通过运行器来实现执行。运行器其实就是测试报告的生成
        unittest中默认的运行器是TextTestRunner

 如何使用测试套件和运行器呢?

  1. 创建测试套件对象
  2. 将测试用例添加到测试套件中
  3. 生成运行器
  4. 运行运行器

具体代码如下:

#创建一个套件对象
suite = unittest.TestSuite()
#1.基于测试用例的名称来进行用例添加
#添加测试用例到一个套件中
suite.addTest(UnitDemo1('test_02_login'))这里是提前准备好的测试用例test_02_login
suite.addTest(UnitDemo1('test_03_failed'))这里是提前准备好的测试用例test_03_failed
#运行器的生成
run = unittest.TextTestRunner()
#运行运行器
run.run(suite)

 我们看一下结果:

测试用例执行状态:
            . 表示PASS
            F 表示Failed


5.2添加测试用例的方法

1. 基于测试用例的名称来进行用例添加

# suite.addTest(UnitDemo('test_02_login'))
# suite.addTest(UnitDemo('test_03_failed')) 

 2. 批量添加测试用例:通过list把所有的用例名称保存,然后进行套件的用例添加

# cases = [UnitDemo('test_02_login'), UnitDemo('test_03_failed'), UnitDemo('test_04_exception')]
# suite.addTests(cases)

 3. 基于class名称来实现用例的添加:将制定的class中所有的测试用例全部添加进来

# suite.addTests(unittest.TestLoader().loadTestsFromTestCase(UnitDemo1)) 

 4. 基于py文件来实现用例的添加:基于py文件的文件名称来实现用例的添加,添加的必须是当前路径下的py文件,不然容易报错。

names = ['unittest_demo.UnitDemo', 'unittest_demo.UnitDemo1']
case = unittest.TestLoader().loadTestsFromNames(names)
suite.addTest(case) 

名称的定义一定要用py文件名称+class名称的形式来实现。


  5. 基于discover方式来实现用例的添加:个人推荐的一种方式,可以批量添加符合规则的文件的所有用例内容,以及搭配持续集成实现自动化持续集成

获取测试用例

#获取测试用例
discover = unittest.defaultTestLoader.discover(start_dir=path,
#运行器的生成                                             pattern='test*.py')
run = unittest.TextTestRunner()
#运行运行器
run.run(discover)
  • 用例的获取路径:path = './'   ./表示当前路径
  •  start_dir表示用例获取的路径
  • pattern表示文件名称
  • discover默认返回一个测试套件

 5.3运行器的测试报告参数

所有的测试套件,都是基于运行器来执行的,所以有套件情况下,必须通过运行器来实现执行。运行器其实就是测试报告的生成 unittest中默认的运行器是TextTestRunner

运行器可以在控制台生成一个简易版的测试报告,这个报告的详略程度我们是可以控制的。主要是通过verbosity参数来进行控制的。

run = unittest.TextTestRunner(verbosity=2)  

默认是1,详细版是2,0不用填写,没有意义

六、HTMLTestRunner测试报告

HTMLTestRunner测试报告生成:UnitTest中非常主流应用的测试报告运行器,不是UnitTest官方自带的。HTMLTestRunner生成html格式的测试报告 。

安装:将文件直接复制到python安装路径下的Lib文件家中:D:\Python\Lib 网上下载的HTMLTestRunner本身默认支持python2.7的版本,要3的版本中使用,必须要修改源码的内容。 不要pip安装

HTMLTestReport测试报告生成,相比较HTMLTestRunner而言,多了一个tester参数,用于描述测试人。除此之外一切相同。 安装:将文件直接复制到python安装路径下的Lib文件家中:

 对mac来说:

在访达中command+shift+G,在弹出的输入框中输入:/资源库/Frameworks/Python.framework/Versions/3.6/lib/python3.6,找到这个目录后把文件放在这个文件夹里面就可以了

 首先我们要配置一些相关信息

我们要提前准备一些信息:保存路径、测试报告的标题、测试报告描述的相关信息、测试报告

文件名称以及完整的路径

report_dir = './report/'  # 保存路径
report_title = '晴天的测试报告'  # 测试报告的标题
report_description = '测试报告中的描述部分的内容'  # 测试报告的描述相关信息
# 测试报告的文件名称及完整路径:为了将每一份测试报告都能够保存下来,建议在用例命名的时候通过时间戳的形式来实现
report_file = report_dir + 'report.html'

然后判断一个测试报告是否存在

if not os.path.exists(report_dir):
    os.mkdir(report_dir)

然后生成一个测试报告,其实也就是文件的写入操作

with open(report_file, 'wb') as file:
    run = HTMLTestRunner(stream=file, title=report_title, description=report_description, verbosity=2)
    run.run(discover)

运行之后,可以看到生成一个测试报告

七、skip装饰器

Skip装饰器的使用,用于做用例的跳过管理,skip装饰器只有三个,用于管理用例是否跳过。
    UnitTest下还有一个特殊的装饰器叫做@unittest.expectedFailure,在运行前已经预期会失败了。默认执行会失败 。

  • 无条件跳过的装饰器
  • 有条件跳过,符合条件,跳过
  • 有条件跳过,与if相反,当条件为False时跳过
import unittest

class UnitDemo(unittest.TestCase):
    #无条件跳过的装饰器
    @unittest.skip('这是无条件跳过')
    def test_01(self):
        print('1')
    #有条件跳过
    @unittest.skipIf(1==1,'这是skipIf的reason')
    def test_02(self):
        print('2')
    #有条件跳过,与if相反,当条件为False时跳过
    @unittest.skipUnless(1==1,'这是skipUnless的reason')
    def test_03(self):
        print('3')

if __name__ == '__main__':
    unittest.main

  • 28
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值