unittest是Python标准库自带的单元测试框架,是Python版本的JUnit,关于unittest框架的使用,官方文档非常详细,网上也有不少好的教程,这里就不多说了。

本文主要分享在使用unittest的过程中,做的一些扩展尝试。先上一个例子。

import unittestclass TestLegion(unittest.TestCase): def test_create_legion(self): """创建军团 :return: """ def test_bless(self): """ 公会祈福 :return: """ def test_receive_bless_box(self): """ 领取祈福宝箱 :return: """ def test_quit_legion(self): """退出军团 :return: """ 

这是一个标准的使用unittest进行测试的例子,写完后心里美滋滋,嗯,就按照这个顺序测就可以了。结果一运行。

什么鬼。执行的顺序乱了。第一个执行的测试用例并不是创建军团,而是公会祈福,此时玩家还没创建军团,进行公会祈福的话会直接报错,导致用例失败。

到这里有些同学会想说,为什么要让测试用例之间有所依赖呢?

的确,如果完全没依赖,测试用例的执行顺序是不需要关注的。但是这样对于用例的设计和实现,要求就高了许多。而对游戏来说,一个系统内的操作,是有很大的关联性的。以军团为例,军团内的每个操作都有一个前提,你需要加入一个军团。所以要实现用例之间的完全解耦,需要每个用例开始之前,检测玩家的军团状态。

如果可以控制测试用例的执行顺序,按照功能玩法流程一遍走下来,节省的代码量是非常可观的,阅读测试用例也会清晰许多。

如何控制unittest用例执行的顺序呢?

我们先看看,unittest是怎么样对用例进行排序的。在loader.pyloadTestsFromTestCase方法里边,调用了getTestCaseNames方法来获取测试用例的名称

def getTestCaseNames(self, testCaseClass): """Return a sorted sequence of method names found within testCaseClass """ def isTestMethod(attrname, testCaseClass=testCaseClass, prefix=self.testMethodPrefix): return attrname.startswith(prefix) and \ callable(getattr(testCaseClass, attrname)) testFnNames = list(filter(isTestMethod, dir(testCaseClass))) if self.sortTestMethodsUsing: testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing)) return testFnNames 

可以看到,getTestCaseNames方法对测试用例的名称进行了排序

testFnNames.sort(key=functools.cmp_to_key(self.sortTestMethodsUsing))

看看排序方法

def three_way_cmp(x, y): """Return -1 if x < y, 0 if x == y and 1 if x > y""" return (x > y) - (x < y) 

根据排序规则,unittest执行测试用例,默认是根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。

做个实验:

import functoolscase_names = ["test_buy_goods", "test_Battle", "test_apply", "test_1_apply"] def three_way_cmp(x, y): """Return -1 if x < y, 0 if x == y and 1 if x > y""" return (x > y) - (x < y) case_names.sort(key=functools.cmp_to_key(three_way_cmp)) print(case_names) output:['test_1_apply', 'test_Battle', 'test_apply', 'test_buy_goods'] 

基于unittest的机制,如何控制用例执行顺序呢?查了一些网上的资料,主要介绍了两种方式:

方式1,通过TestSuite类的addTest方法,按顺序加载测试用例:

suite = unittest.TestSuite()
suite.addTest(TestLegion("test_create_legion"))
suite.addTest(TestLegion("test_bless"))
suite.addTest(TestLegion("test_receive_bless_box"))
suite.addTest(TestLegion("test_quit_legion")) unittest.TextTestRunner(verbosity=3).run(suite) 
 

方式2,通过修改函数名的方式:

class TestLegion(unittest.TestCase): def test_1_create_legion(self): """创建军团 :return: """ def test_2_bless(self): """ 公会祈福 :return: """ def test_3_receive_bless_box(self): """ 领取祈福宝箱 :return: """ def test_4_quit_legion(self): """退出军团 :return: """ 
 

看起来都能满足需求,但是都不够好用,繁琐,代码不好维护。

那就造个轮子吧

于是开始了utx这个小项目,那么如何在不改动代码的情况下,让测试用例按照编写的顺序依次执行呢?

方案就是,在测试类初始化的时候,将测试方法按照编写的顺序,自动依次重命名为“test_1_create_legion”,“test_2_bless”,“test_3_receive_bless_box”等等,从而实现控制测试用例的执行。

这就需要控制类的创建行为,Python提供了一个非常强力的工具:元类,在元类的__new__方法中,我们可以获取类的全部成员函数,另外基于Python3.6的字典底层重构后,字典是有序的了,默认顺序和添加的顺序一致。所以我们拿到的测试用例,就和编写的顺序一致了。

 

接下来,就是按照顺序,依次改名了,定义一个全局的total_case_num变量,每次进行改名的时候,total_case_num递增+1,作为用例的id,加入到用例的名字当中。

    @staticmethoddef modify_raw_func_name_to_sort_case(raw_func_name, raw_func): case_id = Tool.general_case_id() setattr(raw_func, CASE_ID_FLAG, case_id) if setting.sort_case: func_name = raw_func_name.replace("test_", "test_{:05d}_".format(case_id)) else: func_name = raw_func_name return func_name 

接下来是定义自己的TestCase类,继承unittest.TestCase,使用上边定义的元类

class _TestCase(unittest.TestCase, metaclass=Meta): def shortDescription(self): """覆盖父类的方法,获取函数的注释 :return: """ doc = self._testMethodDoc doc = doc and doc.split()[0].strip() or None return doc 

最后一步,对unittest打一个猴子补丁,将unittest.TestCase替换为自定义的_TestCase

unittest.TestCase = _TestCase

看下运行效果,代码和本文开始的例子一样,只是多了一句utx库的导入。

import unittest
from utx import *class TestLegion(unittest.TestCase): def test_create_legion(self): """创建军团 :return: """ def test_bless(self): """ 公会祈福 :return: """ def test_receive_bless_box(self): """ 领取祈福宝箱 :return: """ def test_quit_legion(self): """退出军团 :return: """ 

运行效果:

 

执行顺序就和我们的预期一致了~

基于这一套,开始加上其他的一些扩展功能,比如

  • 用例自定义标签,可以运行指定标签的测试用例
@unique
class Tag(Enum): SMOKE = 1 # 冒烟测试标记,可以重命名,但是不要删除 FULL = 1000 # 完整测试标记,可以重命名,但是不要删除 # 以下开始为扩展标签,自行调整 SP = 2 
class TestLegion(unittest.TestCase):  @tag(Tag.SMOKE) def test_create_legion(self): pass  @tag(Tag.SP, Tag.FULL) def test_quit_legion(self): """退出军团 :return: """ print("吧啦啦啦啦啦啦") assert 1 == 2 
from utx import *if __name__ == '__main__': setting.run_case = {Tag.SMOKE} # 只运行SMOKE冒烟用例 # setting.run_case = {Tag.FULL} # 运行全部测试用例 # setting.run_case = {Tag.SMOKE, Tag.SP} # 只运行标记为SMOKE和SP的用例 runner = TestRunner() runner.add_case_dir(r"testcase") runner.run_test(report_title='接口自动化测试报告') 
  • 数据驱动
class TestLegion(unittest.TestCase):  @data(["gold", 100], ["diamond", 500]) def test_bless(self, bless_type, award): print(bless_type) print(award)  @data(10001, 10002, 10003) def test_receive_bless_box(self, box_id): """ 领取祈福宝箱 :return: """ print(box_id) # 默认会解包测试数据来一一对应函数参数,可以使用unpack=False,不进行解包 class TestBattle(unittest.TestCase):  @data({"gold": 1000, "diamond": 100}, {"gold": 2000, "diamond": 200}, unpack=False) def test_get_battle_reward(self, reward): """ 领取战斗奖励 :return: """ print(reward) print("获得的钻石数量是:{}".format(reward['diamond'])) 
  • 检测测试用例是否编写了说明描述
2017-11-03 12:00:19,334 WARNING legion.test_legion.test_bless没有用例描述 
  • 执行测试用例的时候,显示执行进度
2017-11-03 12:00:19,336 INFO 开始进行测试 2017-11-03 12:00:19,436 INFO Start to test legion.test_legion.test_create_legion (1/5) 2017-11-03 12:00:19,536 INFO Start to test legion.test_legion.test_receive_bless_box (2/5) 2017-11-03 12:00:19,637 INFO Start to test legion.test_legion.test_receive_bless_box (3/5) 2017-11-03 12:00:19,737 INFO Start to test legion.test_legion.test_receive_bless_box (4/5) 2017-11-03 12:00:19,837 INFO Start to test legion.test_legion.test_quit_legion (5/5) 
  • setting类提供多个设置选项进行配置
class setting:# 只运行的用例类型 run_case = {Tag.SMOKE} # 开启用例排序 sort_case = True # 每个用例的执行间隔,单位是秒 execute_interval = 0.1 # 开启检测用例描述 check_case_doc = True # 显示完整用例名字(函数名字+参数信息) full_case_name = False # 测试报告显示的用例名字最大程度 max_case_name_len = 80 # 执行用例的时候,显示报错信息 show_error_traceback = True # 生成ztest风格的报告 create_ztest_style_report = True # 生成bstest风格的报告 create_bstest_style_report = True 
  • 集成 ztest 和 BSTestRunner 生成测试报告,感谢两位作者的测试报告模版

utx库核心源码不到200行,就不做过多讲解了,直接去Github看吧

作者:煎煎煎饼 链接:https://www.jianshu.com/p/d65f97723af7 來源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

转载于:https://www.cnblogs.com/songzhenhua/p/9690198.html

Python3的unittest用例按编写顺序执行相关推荐

  1. python多线程 _thread没有上lock时程序提前给你把可执行的都执行,不按代码编写顺序执行

    下面面这个代码中使用多线程运行线程函数. 使用input函数从终端采集一个字符串,采集字符串的目的是让程序暂停. 目的是在所有的线程执行完之前阻止程序退出. 因为程序无法感知是否有线程正在执行, 是否 ...

  2. Python3+Selenium3+Unittest+ddt+Requests 接口自动化测试框架

    为何选择代码框架进行接口测试? 本文总结分享介绍接口测试框架开发,环境使用python3+selenium3+unittest+ddt+requests测试框架及ddt数据驱动,采用Excel管理测试 ...

  3. Python3 + requests + unittest接口测试

    文章转自 :https://www.jianshu.com/p/75e52b32c76f Python3 + requests + unittest接口测试 12018.09.10 17:23:48字 ...

  4. P202 例9-2 以如图9-8所示的带权有向图为例,编写测试上述图的深度优先和广度优先遍历函数的程序。

    P202 例9-2 以如图9-8所示的带权有向图为例,编写测试上述图的深度优先和广度优先遍历函数的程序. 头文件1:SeqList.h #include<stdio.h>#define M ...

  5. python pip安装指定版本unittest_你们想要的unittest用例失败重运行,解决方案来啦!...

    前言 很多小伙伴一直在诟病 unittest,说 unittest 相对 pytest 来说太鸡肋了,pytest 中提供了很多高级功能 unittest 中都没有. 在这里还是想为 unittest ...

  6. 1.3编程基础之算术表达式与顺序执行 01 A+B问题 (Python3实现)

    http://noi.openjudge.cn/ch0103/01/ https://www.luogu.com.cn/problem/P1001 """ P1001 A ...

  7. 当一个SQL语句同时出现了where,group by,having,order by的时候,执行顺序和编写顺序...

    当一个查询语句同时出现了where,group by,having,order by的时候,执行顺序和编写顺序 1.执行where xx对全表数据做筛选,返回第1个结果集. 2.针对第1个结果集使用g ...

  8. 当一个查询语句同时出现了where,group by,having,order by的时候,执行顺序和编写顺序

    当一个查询语句同时出现了where,group by,having,order by的时候,执行顺序和编写顺序  使用count(列名)当某列出现null值的时候,count(*)仍然会计算,但是co ...

  9. python3 编程入门 100例 1~3

    python3 编程入门 100例 python新手入门必备良药 例1:题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? Created on Thu Aug 2 1 ...

最新文章

  1. HDU2899(二分查找+or+模拟退火算法)
  2. 论电子计算机在审计中的应用,计算机技术在审计中的应用领域分析.doc
  3. swiper4自动轮播切换手动触碰后停止踩坑——属性disableOnInteraction
  4. 学习笔记(18):Python网络编程并发编程-守护进程
  5. 结对项目-小学生四则运算系统网页版项目报告
  6. python会取代前端吗_大家说,python在不远的将来,会不会把java替代,为什么?
  7. 各种门锁的内部结构图_便宜超好用:小米智能门锁 E上手体验报告
  8. Oracle 数据库监听配置
  9. 中国最大照明企业贱卖给外资 创始人遭下狱 刘强东怒斥:有些人没有道德底线!...
  10. css实现居中的各种方法
  11. ArcEngine二次开发_02(鼠标移过图层中显示特定的属性信息)
  12. Google排名第一的编程语言,死磕它这两点,小白也能学的会!不信你看!
  13. 随机变量的数字特征(期望、方差、标准差、矩、协方差...)
  14. 外汇交易:哪个货币对比较好做?
  15. 次世代贴图材质制作的提示和秘籍
  16. java ews_Java---使用EWS读取exchange邮件
  17. 蓝桥杯 并查集汇总学习 及其代码
  18. html项目的致谢词,毕业论文的致谢词范文(精选5篇)
  19. 生成drl文件_drools原生drl规则文件的使用
  20. Ubuntu制作系统ISO镜像

热门文章

  1. Spring Cloud(六) 服务网关GateWay 入门
  2. 补充spring事务传播性没有考虑的几种情况
  3. java简单介绍_java 简单介绍
  4. 四级计算机网络考试大纲,2017年全国计算机等级考试四级计算机网络考试大纲...
  5. ip中继对接_鼎信通达MTG系列-语音中继网关,可和VOS,IPPBX,SIP网关进行对接
  6. 服了,为什么100M宽带还这么卡?
  7. 【收藏】138条 Vim 命令、操作、快捷键全
  8. MSHA x Chaos 容灾高可用实践
  9. 从零入门 Serverless | 一文详解 Serverless 技术选型
  10. android view padding,记一次tablayout的tabView偷偷自带padding的问题