unitTest单元测试框架
一、单元测试的定义:
1:什么是单元测试?
还记不记得我们软件测试学习的时候,按照定义:单元测试就是对单个模块或者是单个类或者单个函数进行测试,一般是开发做的,按照阶段来分,一般就是单元测试、集成测试(接口测试)、系统测试(系统之间的联调测试)、验收测试
2:为什么要做单元测试?
1)单元测试之后,才是集成测试,单个单个的功能模块测试通过之后,才能把单个功能模块集成起来做集成测试,为了从底层发现bug,减少合成后出现的问题。
2)越早发现bug越好,这样可以早点发现问题,不然问题累计到后面,如果做错了就要推倒重来-对于时间和经费来说,是非常浪费的!
3)对于我们测试来说:我们就单元测试是为了执行测试用例!校验程序代码之间的运行逻辑
说白了也就是一句话:单元测试是测代码,相当于白盒测试,检查代码的逻辑以及功能有没有出问题!!!
比如:看接下来的一个例子,测某一个账号密码登录的功能,检查代码有没有问题
def login_check(username=None, password=None): """ 登录校验的函数 :param username: 账号 :param password: 密码 :return: dict type """ if username != None and password != None: if username == 'python31' and password == 'lemonban': return {"code": 0, "msg": "登录成功"} else: return {"code": 1, "msg": "账号或密码不正确"} else: return {"code": 1, "msg": "所有的参数不能为空"}
这段代码是开发人员写的,我们作为测试人员仅仅是去测这些代码有没有问题,怎么去测呢?需要我们自己亲自手写代码
像这个简单的登录案例,一般有这几种情况:
1,账号密码均正确
2,账号正确,密码错误
3,账号错误,密码正确
4,账号为空
5,密码为空
if __name__ == '__main__': # ------------------测试函数正常登录----------------------------------- # 第一步:准备用例数据 expected = {"code": 0, "msg": "登录成功"} data = ("python31", "lemonban") # 第二步:传入参数,获取实际结果 res = login_check(*data) # 第三步:判断用例是否通过(比对预期结果和实际结果) if res == expected: print("用例执行通过!") else: print("用例执行不通过!!!") # ---------------------------测试函数传入错误密码的情况-------------------------------------------- # 第一步:准备用例数据 expected = {"code": 0, "msg": "登录成功"} data = ("python31", "lemonban123") # 第二步:传入参数,获取实际结果 res = login_check(*data) # 第三步:判断用例是否通过(比对预期结果和实际结果) if res == expected: print("用例执行通过!") else: print("用例执行不通过!!!")
# 这些代码是散的,一般不会这样去写,所以我们后面会有更完整的模块代码
unittest四大核心概念:
1,TestCase:测试用例
测试用例类:测试用例以类的形式去定义,并且一定要继承unittest中的unittest.TestCase
测试用例方法:用例类里面的每一个以test开头的方法,就是一条测试用例
2,TestSuite:测试套件 (用来存放测试用例的,相当于一个用例的集合)
3,TestRunner:测试运行程序
4,fixture:测试夹具(测试的前置后置条件处理)
一、TestCase:测试用例
请看下面一段代码:
import unittest from day_13.login import login_check class TestLogin(unittest.TestCase): """登录的测试用例类""" def test_login_pass(self): """登录成功的用例""" # 第一步:准备用例数据 # 1.1用例输入的数据 data = {"username": "python31", "password": "lemonban"} # 1.2预期结果 expected = {"code": 0, "msg": "登录成功"} # 第二步:调入被测的功能函数(请求接口),传入参数 res = login_check(**data) # 第三步:比对预期结果和实际结果(断言) self.assertEqual(expected, res) # 比较第一个参数和第二个参数 if __name__ == '__main__': unittest.main()
上面这段代码运行截图:
因为,我只写了一个测试用例的函数,所以只有1条执行用例显示出来了结果,接下来,我来补充后面几个用例,看看运行跑一下会有什么样的结果:
注意:如果你的代码报错,无法导入unittest,unittest那里标记红色波浪线,那就说明你一开始没有导入,现在教你一个快速安装的方法:看下面的截图:
好的,继续,接下来,我来补充后面几个用例,看看运行跑一下会有什么样的结果:
class TestLogin(unittest.TestCase): """登录的测试用例类""" def test_login_pass(self): """账号密码均正确,登录成功""" # 第一步:准备用例数据 # 1.1用例输入的数据 data = {"username": "python31", "password": "lemonban"} # 1.2预期结果 expected = {"code": 0, "msg": "登录成功"} # 第二步:调入被测的功能函数(请求接口),传入参数 res = login_check(**data) # 第三步:比对预期结果和实际结果(断言) self.assertEqual(expected, res) def test_login_pwd_error(self): """密码错误""" # 第一步:准备用例数据 # 1.1用例输入的数据 data = {"username": "python31", "password": "lemonban123"} # 1.2预期结果 expected = {"code": 1, "msg": "账号或密码不正确"} # 第二步:调入被测的功能函数(请求接口),传入参数 res = login_check(**data) # 第三步:比对预期结果和实际结果(断言) self.assertEqual(expected, res) def test_login_user_error(self): """账号错误""" # 第一步:准备用例数据 # 1.1用例输入的数据 data = {"username": "python999", "password": "lemonban"} # 1.2预期结果 expected = {"code": 1, "msg": "账号或密码不正确"} # 第二步:调入被测的功能函数(请求接口),传入参数 res = login_check(**data) # 第三步:比对预期结果和实际结果(断言) self.assertEqual(expected, res) def test_login_user_is_none(self): """账号为空""" # 第一步:准备用例数据 # 1.1用例输入的数据 data = {"username": "", "password": "lemonban"} # 1.2预期结果 expected = {"code": 1, "msg": "所有的参数不能为空"} # 第二步:调入被测的功能函数(请求接口),传入参数 res = login_check(**data) # 第三步:比对预期结果和实际结果(断言) self.assertEqual(expected, res) def test_login_pwd_is_none(self): """密码为空""" # 第一步:准备用例数据 # 1.1用例输入的数据 data = {"username": "python31", "password": ""} # 1.2预期结果 expected = {"code": 1, "msg": "所有的参数不能为空"} # 第二步:调入被测的功能函数(请求接口),传入参数 res = login_check(**data) # 第三步:比对预期结果和实际结果(断言) self.assertEqual(expected, res) if __name__ == '__main__': unittest.main()
我一共写了5条用例,在上面的代码中,那看看运行结果截图:
那具体怎么看没通过的用例?点击即可
说明开发人员写的这段代码是有问题的,可有的人会问,是哪段代码有问题,我怎么看不出,这个不必深究,反正咱们测试人员已经测出bug了,代码的逻辑问题,需开发人员自己去修改,
附上那段有问题的代码,也就是最前面的那段:
二、TestSuite:测试套件
TestSuite:测试套件 (用来存放测试用例的,相当于一个用例的集合)
如果用例一旦数量较多,比如20个,30个,那还会像上面这样一个一个去写吗?不会,此时,我们可以用到Testsuite这个测试套件,具体的步骤接下来看我慢慢讲解:
首先我们需要在项目中建一个文件夹File-->dirctory,命名为testcase,然后在这个专门存放测试用例的文件夹下,把用例按照模块去写进去,比如,我们要测试登录这个功能模块,那我们命名为test_login
如果要测试注册功能,就命名为test_register,如果要测试退出功能,就命名为test_exit,这里的每一个命名都必须以test开头,然后,比如在测试登录模块中,我们写了5个用例,当然,在实际的项目中,可以根据不同的需求
来在每一个你要测试的模块中写多个用例,我们这里是相当于把功能先分开,再在每个功能里写多个用例,如果需要执行用例,后面可以直接调用来跑程序看结果
当然,我们也得单独在项目中新建一个run.py文件,这个是专门用来运行程序的
再来看看run.py文件中的代码:
import unittest from unittestreport import TestRunner # 第一步:创建一个测试套件 suite = unittest.TestSuite() # 第二步:将测试用例添加到套件中 # 2.1 创建加载器: loader = unittest.TestLoader() # 2.2加载用例到套件中 # 第一种:通过测试用例类去加载 from day_13.testcase.test_login import TestLogin suite.addTest(loader.loadTestsFromTestCase(TestLogin)) # 第三步:执行测试用例类 runner = TestRunner(suite) runner.run()
其中第二步:将测试用例加载到套件中,一共有三种方式,这里我先讲第一种,但是第一种用的不多,待会我3种都会讲,其中第三种用的是最多的,工作种也常用的。
那么运行一下,看看截图效果:
再点击.report.html,运行看测试报告:
因为这份测试报告,有点长,不方便一次截图成功,所以我就截图3次,展示出来,这样看起来,是不是感觉高大上的样子,没错,这就是用unittest这个测试框架的好处,很秀很秀!!!
这仅仅是其中一种风格,还有另外一种风格,
看看另外一种的风格:
所以,我们在run.py文件中,可以自由设置这些生成报告的参数。
继续,上面只讲了一种方式,另外两种方式去加载用例,先上代码,再看截图解析:
""" 用例加载的过程: 1、先找指定路径中test开头的python文件 2、再找Test开头文件中继承unittest.TestCase的用例类 3、再去找用例类中以test开头的方法 测试用例执行的顺序 1、模块按照ASCII码排序 2、类名按照ASCII码排序 3、方法名按照ASCII码排序 """ import unittest # 第一步:创建一个测试套件 suite = unittest.TestSuite() # 第二步:将测试用例添加到套件中 # 2.1 创建加载器: loader = unittest.TestLoader() # 2.2加载用例到套件中 # 第一种:通过测试用例类去加载 # from day_13.testcase.test_login import TestLogin # suite.addTest(loader.loadTestsFromTestCase(TestLogin)) # 第二种:通过测试用例模块去加载 # from day_13.testcase import test_login # suite.addTest(loader.loadTestsFromModule(test_login)) # 第三种:指定用例所在文件路径去加载(工作中常用!!!) suite.addTest(loader.discover(r"C:\Users\Administrator\PycharmProjects\example\day_13\testcase")) # 第三步:执行测试用例 runner = unittest.TextTestRunner() runner.run(suite)
三、TestRunner: 测试运行程序
这部分的内容,主要是用来执行测试用例和生成测试报告的,先看代码,再看截图:
import unittest from unittestreport import TestRunner # 第一步:创建一个测试套件 suite = unittest.TestSuite() # 第二步:将测试用例添加到套件中 # 2.1 创建加载器: loader = unittest.TestLoader() # 2.2加载用例到套件中 # 第一种:通过测试用例类去加载 from day_13.testcase.test_login import TestLogin suite.addTest(loader.loadTestsFromTestCase(TestLogin)) # 第三步:执行测试用例类 runner = TestRunner(suite, filename="report.html", report_dir=".", title='测试报告', tester='excellent', desc="excellent执行测试生成的报告", templates=1 # 这里可以生成两个风格不同的报告,待会给大家看一下 ) runner.run()
这个run.py主要是和测试套件suite一起用,用来运行程序,
四、fixture测试夹具
fixture:测试用例环境的搭建与销毁,测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)
setUpClass(只执行一次)
setUp
tearDown
tearDownClass(只执行一次)
import unittest class TestRegister(unittest.TestCase): def test_01(self): """比对1和100""" print("-----------执行test_01------------") self.assertEqual(100, 100) def test_02(self): """比对1000和1000""" print("-----------执行test_02------------") self.assertEqual(1000, 1000) def setUp(self): # 每条测试用例执行之前都会调用该方法(有多少用例执行多少次) print("-------------setup----------") def tearDown(self): # 每条测试用例执行之后都会调用该方法(有多少用例执行多少次) print("-------------tearDown----------") @classmethod def setUpClass(cls): # 测试类里面的用例执行之前会调用该方法(只会执行一次) print("-------------setUpClass----------") @classmethod def tearDownClass(cls): # 测试类里面的用例执行之后会调用该方法(只会执行一次) print("-------------tearDownClass----------") if __name__ == '__main__': unittest.main()
未完待续。。。。。。。。。。。。。。。。。。。。。。。