unittest(一)---基础

一、unittest

unittest是python自带的单元测试框架,基于java的junit测试框架设计。

unittest的重要组件:testCase测试用例、testSuite测试套件、testFixture测试固件(夹具)、testLoader测试加载器、testRunner测试运行器。

unittest单元测试框架的功能:

  • 测试发现:从多个py文件中发现并且加载测试用例
  • 测试执行:将测试用例按照一定的顺序和条件执行并生成结果
  • 测试判断:用断言去判断结果是否正确
  • 测试报告:生成测试报告,展示测试结果、通过率等

二、testCase测试用例

1.测试用例及执行

测试用例类必须继承基类unittest.TestCase,测试用例方法必须以test开头

import unittest

class testRegister(unittest.TestCase):
    
    def test01_user(self):
        print("username")

    def test02_psw(self):
        print("password")

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

运行结果

unittest.main()会执行所有test开头的方法(测试用例),如果方法不以test开头将不会被主动执行。

测试用例的执行不是以代码顺序执行,而是以方法名称的ASCII码顺序执行。

2.执行结果

执行结果:.成功,F失败,E错误,s跳过

import unittest

class testRegister(unittest.TestCase):
    
  #成功
def test01_user(self): print("username")
  #失败
def test02_psw(self): print("password") self.assertTrue(0)
  #错误
def test04_register(self): print("register") raise Exception("自定义异常")
#跳过 @unittest.skip(
"跳过此用例") def test03_psw_again(self): print("password again") if __name__ == "__main__": unittest.main()

 3.框架的执行逻辑

 

  • module='__main__‘  :测试用例所在的位置,__main__指当前模块
  • defaultTest=None :待测测试用例的名称,默认是所有;可指定执行部分测试用例
  • argv=None :接收外部的参数
  • testRunner :测试运行器,默认使用TextTestRunner
  • testLoader=loader.defaultTestLoader :测试用例加载器
  • exit=True :执行完测试用例后是否退出Python程序;默认退出
  • verbosity=1 :输出测试结果的详细程度,有0、1、2,越来越详细;默认为1
if __name__ == "__main__":
  #设置执行测试用例和输出结果的详细程度 unittest.main(defaultTest
=["testRegister.test01_user","testRegister.test02_psw"],verbosity=2)

4.命令行方式运行

python -m unittest fileName.className.functionNane -v  执行某文件的某类的某个测试用例,也可以写多个文件,用空格隔开

  • python -m 将库中的模块用脚本的方式执行
  • -v 详细的输出测试结果

python -m unittest discover -s dirName -p "*.py"  执行dirName文件夹下的py文件;文件名必须包含test(大小写皆可)

python -m unittest -h 查看更多参数

 

 三、testSuite测试套件

将测试用例加入到测试套件里,可以选择执行部分测试用例;执行顺序按照加入测试条件的顺序执行

1.测试套件的构建方法

1.1 单个添加测试用例
import unittest

class testRegister(unittest.TestCase):
    
    def test01_user(self):
        print("username")

    def test02_psw(self):
        print("password")

    def test04_register(self):
        print("register")

    def test03_psw_again(self):
        print("password again")


if __name__ == "__main__":
    #创建一个测试套件
    suite = unittest.TestSuite()
    #测试套件增加测试用例
    suite.addTest(testRegister("test02_psw"))
    suite.addTest(testRegister("test01_user"))
    #指定执行套件里的测试用例
    unittest.main(defaultTest="suite")

 1.2 多个添加测试用例
if __name__ == "__main__":
    suite = unittest.TestSuite()
    cases = [testRegister("test02_psw"),testRegister("test01_user")]
    #一次添加多个测试用例
    suite.addTests(cases)
    #以TextTestRunner()的方式运行
    unittest.TextTestRunner(verbosity=2).run(suite)

2.测试加载器构建

import unittest
import os

if __name__ == "__main__":
    #使用测试加载器加载测试用例
    #也可写作unittest.TestLoader().discover()   
    #discover(self: TestLoader, start_dir: str, pattern: str, top_level_dir: str) -> TestSuite
    #加载当前目录下,test开头的py文件的全部测试用例
    suite = unittest.defaultTestLoader.discover(start_dir=os.getcwd(), pattern="test*.py")
    unittest.main(defaultTest="suite")

3.跳过测试用例

  • @unittest.skip(reason):无条件跳过测试用例,并说明原因(如果verbosity=2会打印出原因,如果是1只会打印s);放在类前面就是跳过类下面的所有测试用例,放在单个测试用例前面,就是跳过此测试用例
  • @unittest.skipIf(condition,reason):如果条件为真时就跳过测试用例
  • @unittest.skipUnless(condition,reason):如果条件为假时就跳过测试用例
  • @unittest.expectedFailure:预期该测试用例结果为失败,如果测试用例失败则不记为失败,记为expected failure(如果verbosity=1,记为x),如果测试用例成功则记为unexpected success(如果verbosity=1,记为u)
import unittest

class testRegister(unittest.TestCase):
    
    @unittest.skip("跳过此用例")
    def test01_user(self):
        print("username")

    @unittest.skipIf(1<2,"如果1小于2就跳过此用例")
    def test02_psw(self):
        print("password")

    @unittest.skipUnless(1>2,"如果1不大于2就跳过此用例")
    def test03_psw_again(self):
        print("password again")

@unittest.skipIf(1 != 2, "如果1不等于2就跳过类里面的所有用例" )
class testRegister2(unittest.TestCase):
    
    def test01_user2(self):
        print("username")

    def test02_psw2(self):
        print("password")

if __name__ == "__main__":
    unittest.main(verbosity=2)

 

import unittest

class testRegister(unittest.TestCase):
    
    @unittest.expectedFailure
    def test01_user(self):
        print("username")
        self.assertTrue(0)  #失败

    @unittest.expectedFailure
    def test02_psw(self):
        print("password")
        self.assertTrue(1)  #成功


if __name__ == "__main__":
    unittest.main(verbosity=2)

 

四、testFixture测试固件

  • setUp/tearDown:在每个测试用例之前/之后运行一遍;主要用于打开浏览器,加载网页等/关闭网页
  • setUpClass/tearDownClass:在每个类之前/之后运行一遍;主要用于创建数据库连接、创建日志对象/关闭数据库连接、销毁日志对象
  • setUpModule/tearDownModule:在每个模块之前/之后运行一边
import unittest

class testRegister(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("在类的所有用例开始前运行")
    
    @classmethod
    def tearDownClass(cls):
        print("在类的所有用例结束后运行")
    
    def setUp(self):
        print("在每个测试用例之前运行")

    def tearDown(self):
        print("在每个测试用例之后运行")
    
    def test01_user(self):
        print("username")

    def test02_psw(self):
        print("password")

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

 

import unittest

def setUpModule():
    print("在模块开始之前运行")

def tearDownModule():
    print("在模块结束之后运行")

class testRegister(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        print("在类的所有用例开始前运行")
    
    @classmethod
    def tearDownClass(cls):
        print("在类的所有用例结束后运行")
    
    def test01_user(self):
        print("username")

    def test02_psw(self):
        print("password")

class testRegister2(unittest.TestCase):
    
    def test01_user2(self):
        print("username")

    def test02_psw2(self):
        print("password")

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

 

 

五、断言

断言用于校验实际结果与预期结果是否匹配;(断言内容必须选择核心内容,需要能明确表示是否成功的因素;比如用title判断是否跳转到网页,可能title已经是了,但是因为网页问题内容并没有加载出来)

1.常用断言

方法 检查
assertEqual(a,b) a == b
assertNotEqual(a,b) a != b
assertTrue(x) bool(x) is True
assertFalse(c) bool(x) is False
assertIn(a,b) a in b
assertNotIn(a,b) a not in b
assertIs(a,b) a is b
assertIsNot(a,b) a is not b
assertIsNone(x) x is none
assertIsNotNone(x) x is not none

 

 

 

 

 

 

 

 

 

 

 

 

2.简单例子

import unittest

class testRegister(unittest.TestCase):

    def test01_user(self):
        self.assertEqual("username","username")

    def test02_pws(self):
        self.assertIn("pws","password")

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

六、HTMLTestRunner

HTMLTestRunner是unittest的一个第三方库,可以生成简单明了的html格式的报告。本文使用的HTMLTestRunner来自https://github.com/Gelomen/HTMLTestReportCN-ScreenSho,下载后将py文件改名为HTMLTestRunner.py,并放在python\Lib\site-packages目录下。

import unittest
import HTMLTestRunner
import os
import time

class testRegister(unittest.TestCase):

    def test01_user(self):
        """这个用例会成功"""
        self.assertEqual("username","username")

    def test02_pws(self):
        """这个用例会失败"""
        self.assertIn("pws","password")

if __name__ == "__main__":
    suite = unittest.TestLoader().discover(start_dir="./",pattern="test*")
    nowtime = time.strftime("%Y-%m-%d %H-%M-%S", time.localtime())
    fp = open(os.getcwd()+"\\"+nowtime+"report.html","wb")
    runner = HTMLTestRunner.HTMLTestRunner(stream=fp,title="测试报告",description="自动化测试报告",tester="测试")
    runner.run(suite)
    fp.close()

 

七、ddt

ddt(data-driven test)数据驱动测试,可以实现不同数据运行同一个测试用例。数据和测试用例分离,避免编写重复测试脚本。通过数据驱动,来验证多组数据测试场景。

使用ddt需先安装,pip install ddt

ddt模块包含一个类装饰器ddt和3个方法装饰器

  • ddt:装饰继承TestCase的类
  • data:装饰方法,参数是一系列的值,可以是字符串数字等,也可以是列表元组字典等;一次只传递一个参数,列表元组字典会当作一个参数传递,在前面添加*会拆分成多组传递
  • file_data:装饰方法,参数是文件;会从yaml或json中加载数据;如果是.yml或.yaml文件以yaml格式加载数据,其他文件都以json格式加载数据
  • unpack:如果传递的是复杂的数据结构时,如列表元组字典,添加unpack会把列表元组字典拆分成多个参数传递

1.data 

import unittest
from ddt import ddt,data,unpack

@ddt
class testDdt(unittest.TestCase):

    @data(1,2,3,[4,5])
    def test_ddt(self,a):  #只能有一个参数
        print(a)

if __name__ == "__main__":
    unittest.main(verbosity=2)

#如果参数是一个数组
import unittest
from ddt import ddt,data,unpack

@ddt
class testDdt(unittest.TestCase):

    @data([1,2,3,4])
    def test_ddt(self,a):  #只能有一个参数
        print(a)

if __name__ == "__main__":
    unittest.main(verbosity=2)

 

#如果列表前面那加了*;如果是字典前面加了*,打印出来的a的值是字典的key
import unittest
from ddt import ddt,data,unpack

@ddt
class testDdt(unittest.TestCase):

    @data(*[1,2,3,4])
    def test_ddt(self,a):  #只能有一个参数
        print(a)

if __name__ == "__main__":
    unittest.main(verbosity=2)

 

 2.unpack

#unpack
import unittest
from ddt import ddt,data,unpack

@ddt
class testDdt(unittest.TestCase):

    @data([1,2],[3,4])
    @unpack
    def test_ddt(self,a,b):  #2个参数,如果传入的data是字典,参数名称必须与data的key相同
        print(a,b)

if __name__ == "__main__":
    unittest.main(verbosity=2)

 

3.ddt处理yaml格式

首先要安装pip install PyYAML,用于处理yaml文件;关于yaml文件格式基础知识:https://www.runoob.com/w3cnote/yaml-intro.html

import unittest
from ddt import ddt,file_data

@ddt
class testDdt(unittest.TestCase):

    @file_data("testdata.yaml")
    def test_ddt(self,**a):
        print(a)
        print(type(a))   #字典格式
        print(a.get("id"))
   
    #@file_data("testdata.yaml")
    #def test_ddt(self,id,name,price):  #也可以用字典的key来接受数据
        #print(id,name,price)   #test_ddt_00001 ... 001 icecream 100

if __name__ == "__main__":
    unittest.main(verbosity=2) 

 

posted @ 2022-02-26 22:49  小测试00  阅读(83)  评论(0编辑  收藏  举报