新书

速递

吴老的java版《selenium webdriver 实战宝典》和python版《selenium Webdriver 3.0 自动化测试框架实战指南》出版了,代码拿来就能用。

文 |  iTesting

来源 | 微信公众号 iTesting

自动化测试用例写多了,不可避免会遇到这个问题,每次运行无需运行所有的用例,那么如何把要运行的用例挑出来并高效组织它们呢?

一般说来,通用的做法都是把要运行的用例用特殊标记Mark出来,然后框架运行时,自动寻找这些带标记的case,并把它们装到一个新的test suite里。

所以高效组织测试用例的关键就是两部分:

1. 如何标记待测试用例.

2. 运行时如何收集这些带标记的用例。

以下列出来我经历过的方法:

1.给所有的测试用例编号,把测试用例按照 编号, summary, Path, ClassName, 测试运行与否的flag等等,写到一个外部文件里,通常是Excel。

每次测试框架运行时,先去这个文件里,一行一行的读文件,如果发现“测试运行与否的flag”的值是True, 就把这条case拿出来放到一个临时变量里,读完整个文件,就拿到了

所有要运行的case, 这个时候,导入这个case需要的所有依赖,运行这个case,并把运行结果写回到一个results的文件里。 那么,改动老的case,增加新的case,怎么办呢?

勤快一点的,每写一个/更改一个自动化用例,手工更新这个外部表格。 懒一点的,写一段代码,每写/改一个case,手工运行这段代码,把更改写进去。

2.利用装饰器,经典的如TestNG,可以在测试用例前加@BeforeSuite, 今天要讲的也是这个。如何在python里实现。

先说下思想: 我期望自动化测试运行时候,可以根据我提供的不同的tag,运行不同的测试用例。 前提是我写自动化用例的时需给每个测试用例,都加上tag并赋值。

在运行时候,框架通过我提供的不同的tag,去找这些被标记的case,然后组织起来运行。

要实现这个,我们需要接收用户参数,可以用:

1.sys.argv[1:]来自己写代码处理,也可以用标准库argparse来,我们用后者。

argparse 的用法很直观, 先看段代码::

1. 先创建一个parser:

parser = argparse.ArgumentParser(prog='para')

2. 加入一些参数:

parser.add_argument('-w', action='store', default='.', help='specify the work space')

parser.add_argument('-target', action='store', help='specify the run target file')

parser.add_argument('-tag', action='store', help='run by tag')

parser.add_argument('-n', action='store', help='concurrent user')

3. Parseing 这些参数

options = parser.parse_args()

print (options)

这个时候,如在command line里输入

python __init__.py -target test -tag smoke -n 3

就会得到如下结果:

Namespace(n='3', tag='smoke', target='test', w='.')

使用argparse就可以方便的获取用户参数,并处理。关于argparse的用法,请参考官网。

2.用户输入的参数我们接收处理了,那么如何处理这些tag呢,这就要用到装饰器:

def TestCase(enabled=True, tags=None, **kwargs):

def tracer(cls):

cls.__type__ = 'Test'

cls.__enabled__ = enabled

if not tags:

cls.__tags__ = None

else:

# 想想看,如果tag有好几个,这块代码应该怎么改

cls.__tags__ = tags

return cls

return tracer

上面的代码呢,我们定义了一个装饰器,这个装饰器唯一的作用,是接收传入的类,然后给这些类增加一些属性,这些属性后续用来判断是否要执行。 使用时在新建立测试类上加上@Testcase即可。

3.测试用例类,这些测试类或者测试类中的函数实现了一个个的功能。

class RealTest():

@TestCase(tags='smoke')

def test1(self):

logging.info("this is test 1")

time.sleep(5)

@TestCase(enabled=True, tags='smoke')

def test2(self):

logging.info("this is test2")

time.sleep(5)

@TestCase(enabled=False, tags='smoke')

def test3(self):

logging.info("this is test3")

time.sleep(5)

我们可以利用装饰器里的tag参数, enable参数来实现测试用例的筛选。

4.我们再定义一个类,这个类作为一个test case的执行器,把要运行的测试用例装进来,并发,或者顺序执行。

class TestExecutor(threading.Thread):

def __init__(self, cls, worker):

threading.Thread.__init__(self)

self.cls = cls

self.workers = worker

self._lock = threading.RLock()

def run(self):

self.workers.acquire()

logging.info("Thread {threadname}".format(threadname=self.current_executor()))

self.cls(self)

self.workers.release()

def current_executor(self):

return threading.current_thread()

5.利用threading.Semaphore实现线程并发限制。

6.实现:

import argparse

import threading

import time

import logging

logging.basicConfig(level=logging.INFO , format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')

def TestCase(enabled=True, tags=None, **kwargs):

def tracer(cls):

cls.__type__ = 'Test'

cls.__enabled__ = enabled

if not tags:

cls.__tags__ = None

else:

# 想想看,如果tag有好几个,这块代码应该怎么改

cls.__tags__ = tags

return cls

return tracer

class TestExecutor(threading.Thread):

def __init__(self, cls, worker):

threading.Thread.__init__(self)

self.cls = cls

self.workers = worker

self._lock = threading.RLock()

def run(self):

self.workers.acquire()

logging.info("Thread {threadname}".format(threadname=self.current_executor()))

self.cls(self)

self.workers.release()

def current_executor(self):

return threading.current_thread()

class RealTest():

@TestCase(tags='smoke')

def test1(self):

logging.info("this is test 1")

time.sleep(5)

@TestCase(enabled=True, tags='smoke')

def test2(self):

logging.info("this is test2")

time.sleep(5)

@TestCase(enabled=True, tags='smoke')

def test3(self):

logging.info("this is test3")

time.sleep(5)

if __name__ == "__main__":

s = threading.Semaphore(2)

parser = argparse.ArgumentParser(prog='para')

parser.add_argument('-w', action='store', default='.', help='specify the work space')

parser.add_argument('-target', action='store', help='specify the run target file')

parser.add_argument('-tag', action='store', help='run by tag')

parser.add_argument('-n', action='store', help='concurrent user')

options = parser.parse_args()

real_cases = []

for module_element in  dir(RealTest):

pre_test_class = getattr(RealTest,module_element) # @Mark

if options.tag:

if hasattr(pre_test_class, "__type__") and \

hasattr(pre_test_class, "__enabled__") \

and pre_test_class.__enabled__\

and hasattr(pre_test_class, "__tags__")\

and pre_test_class.__tags__ \

and pre_test_class.__tags__.lower() == options.tag.lower():

real_cases.append(pre_test_class)

threads = []

for item in real_cases:

threads.append(TestExecutor(item, s))

for t in threads:

t.start()

for t in threads:

t.join()

这里重点讲下标记为

#@Mark

的语句,实际上这个就是如何把标记的case挑选出来的核心方法。

我们使用了反射机制

反射: 有时候我们会碰到这样的需求,需要执行对象的某个方法,或是需要对对象的某个字段赋值,而方法名或是字段名在编码代码时并不能确定,需要通过参数传递字符串的形式输入。举个具体的例子:当我们需要实现一个通用的DBM框架时,可能需要对数据对象的字段赋值,但我们无法预知用到这个框架的数据对象都有些什么字段,换言之,我们在写框架的时候需要通过某种机制访问未知的属性。这个机制被称为反射(反过来让对象告诉我们他是什么),或是自省

dir([obj]):

调用这个方法将返回包含obj大多数属性名的列表(会有一些特殊的属性不包含在内)。obj的默认值是当前的模块对象。

hasattr(obj, attr):

这个方法用于检查obj是否有一个名为attr的值的属性,返回一个布尔值。

getattr(obj, attr):

调用这个方法将返回obj中名为attr值的属性的值,例如如果attr为’bar’,则返回obj.bar。

对于所有的测试类,我们会逐个把他们导入(示例里无这段代码,关于case class的导入可以根据 -target这个参数的值来代码实现,此处未展示。)

对于每一个类,我们通过dir(), getattr()拿到测试类所有的可用方法,然后这些方法因为有装饰器TestCase的加持,我们可以顺利的根据参数-tags拿到要运行的测试用例。

然后我们根据拿到的每一个测试用例,判断它们是否有需要的属性( enabled,type), 然后对于每个有这个属性的用例,我们在放到真正运行的测试用例list里。

最后对测试用例list的用例,每一个都放入TestExecutor里执行,TestExecutor 实现了多线程及多线程限制。

我们来看下运行效果:

输入:

python Android/init.py -target test -tag smoke -n 3

输出:

因为我们限制了并发线程数为2, 所以我们先看到以下输出。

Namespace(n='3', tag='smoke', target='test', w='.')

2017-07-07 20:01:25,009 - __init__.py[line:31] - INFO: Thread

2017-07-07 20:01:25,009 - __init__.py[line:42] - INFO: this is test 1

2017-07-07 20:01:25,009 - __init__.py[line:31] - INFO: Thread

2017-07-07 20:01:25,010 - __init__.py[line:47] - INFO: this is test2

5秒后,又看到如下:

2017-07-07 20:01:30,010 - __init__.py[line:31] - INFO: Thread

2017-07-07 20:01:30,011 - __init__.py[line:52] - INFO: this is test3

如此一来,我们仅需要写测试用例时,添加合适的tag,然后运行时用参数区分,就可以不修改代码的的情况下运行不同的case了。

3年,服装设计转测试开发!How?

来自测试人的困惑与思考

大龄 | 手工 | 自动化逆袭

【appium实战】appium混合页面点击方法tap的使用

实战:微信小程序+appium测试实例

实战:微信公众号+appium测试实例

使用LR编写windows sockets协议xml报文格式脚本

Python实战:file tell()返回的指针怎么就不一样?

互联网架构的演变

爬虫之我与正则的甜蜜约会

草根在测试行业如何杀出一条血路(8)

2018web测试开发培训一年期周六班!

在喜马拉雅app搜索并收听“光荣之路”电台光荣之路招聘|征稿|合作|QQ群735821166@qq.compython群:457561756性能群:415987441招聘群:203715128感谢认真阅读的你!

测试用例怎么写_如何高效组织自动化测试用例相关推荐

  1. 做python自动化得时候怎么添加断言_在编写Web自动化测试用例的时候,如何写断言使新手不解,严格意义上来讲,没有断言的自动化脚本不能叫测试用例。就像功能测试一样,当测试人员做了一些操作...

    在编写Web自动化测试用例的时候,如何写断言使新手不解,严格意义上来讲,没有断言的自动化脚本不能叫测试用例.就像功能测试一样,当测试人员做了一些操作之后必然会判断实际结果是否等于预期结果,只不过,这个 ...

  2. uat测试用例怎么写_你会写测试用例吗

    作为一名测试工程师,写测试用例作为一项最最基本的技能谁不会啊!但就是这最基本的技能也会存在很多问题,今天就跟大家分享下写测试用例这件事情上存在的的一些问题和对应的思考: 为什么要写测试用例啊,测试用例 ...

  3. swagger python自动化用例_自动生成robot自动化测试用例

    #!/usr/bin/env python """解析swagger接口返回值自动生成接口自动化用例 将新增的用例写入到文件末尾,已存在的用例则不重新写入"&q ...

  4. 自动化测试用例设计的原则

    1.自动化测试用例范围往往是核心业务.流程或者重复执行率较高的. 2.自动化测试用例的选择一般以"正向"为主. 3.不是所有手工用例都可以使用自动化测试来执行. 4.手工测试用例往 ...

  5. 【转】自动化测试用例设计的原则

    很多公司在实施自动化测试的过程中,往往会把所有的手工测试用例作为自动化测试用例,并且直接进行脚本的开发工作,甚至有些公司不写自动化测试用例,直接想当然地开发测试脚本,这些都是极其不规范的做法,甚至很有 ...

  6. php接口自动化测试用例编写

    最近用php写完了一版项目的接口,代码有点多,意味着bug也会很多,人工测试起来有点麻烦,于是准备用php编写一个测试bug的程序.以前是没有这种意识的.这篇文章主要是提醒我未来程序写完后,能养成编写 ...

  7. python写ui自动化测试用例_自动化测试(6) | Web UI 自动化测试方案

    Web项目的 UI 自动化测试方案 有用的链接: 项目讨论 项目中符合自动化测试的部分有哪些?(目标和范围 scope, 准入准出标准) 稳定的需求点.变动较少的页面 每日构建后的测试验证 daily ...

  8. 如何写出高效的软件测试用例?

    如何写出高效的软件测试用例? 一级目录 二级目录 三级目录 一.什么是测试用例 二.如何编写测试用例 1.看怎样去实现覆盖需求所有测试点 2.挖掘需求外测试点 三.分享测试用例专项视频供大家学习 一级 ...

  9. python自动卸载win程序_朋友说:能不能用python,帮我写一个“制作工资条”的自动化程序...

    本文说明 今天和一个朋友吃饭,她说我经常使用Excel制作工资条,但是每个月都要做一遍,你能不能用python写一个代码,能够自动化完成这个工作.这当然可以啦,就是这么牛逼! 我们先来看看原始数据是什 ...

最新文章

  1. java struts技术_java技术框架之:struts
  2. php调用其它控制器,TP框架控制器里面怎么调用另一个控制器的代码
  3. Linear Regression总结2
  4. 科大星云诗社动态20210223
  5. Jenkins 关闭和重启实现方式.
  6. Ajax异步(客户端测试)
  7. 02331 数据结构 二叉树的遍历
  8. trigger_name 的命名规范
  9. jsp不能使用return时候,如何在出异常时退出,不在向下执行
  10. CCFollow和ActionCallFunc
  11. 批处理学习笔记1 - Hellow World
  12. jsecharts如何改变占比_什么是基础代谢?如何提高基础代谢?基础代谢对减肥很重要...
  13. c#垂直投影法_形象理解“梯度”与“法向量”的关系
  14. WiFi基础知识概述
  15. jquery学习笔记
  16. 金庸年度最新十大广告
  17. 最新时空观测结果证实爱因斯坦相对论合理性
  18. HP台式计算机不能启动,惠普电脑不能启动怎么处理
  19. Python图片文字识别
  20. 手把手 教你如何做网线接头

热门文章

  1. 谷歌官宣安卓改名!甜点不再
  2. 深入掌握JMS(二):一个JMS例子
  3. 标签管理体系之业务应用
  4. 用纯css来实现一个优惠券
  5. hdu 5380 Travel with candy(双端队列)
  6. java计算集合对称差
  7. 构建模式--Adapter模式(JAVA)
  8. 云计算市场多元并立,唯有合作才能共赢
  9. 分布式任务调度框架hanzelcast使用
  10. 难道网上就没有真正完全无误的Postfix教程了吗?