appium android app 用户登录时输入密码 没动静或者值错误,输入中文异常的解决方法!

fdffd58 回复了问题 • 2 人关注 • 4 个回复 • 3345 次浏览 • 2018-08-01 23:36 • 来自相关话题

图形验证码-AI人工智能初步探索

兵临咸阳 发表了文章 • 0 个评论 • 317 次浏览 • 2018-06-28 17:10 • 来自相关话题

处于安全考虑,大部分的网站登录页面都会有验证码,验证码的有简单也有复杂的;
以下是度娘对验证码识别的介绍
验证码的主要目的是强制人机交互来抵御机器自动化攻击的。大部分的验证码设计者并不得要领,不了解图像处理,机器视觉,模式识别,人工智能的基本概念。
看来,验证码识别涉及到人工智能,这篇文章,通过验证码识别,对人工智能进行一个初探。
python robot框架有图像处理库PIL、pytesser、tesseract
环境搭建
1.pillow命令安装
pip install Pillow
用pip命令安装不了的话,也可以去官网下载whl包进行安装
https://pypi.org/project/Pillow/
安装完毕,需要检查一下安装是否成功。
如下图所示,打开python后,输入import Image,会提示没有这个模块。因为使用pillow库需要使用from PIL import Image代替import Image。按照图中的处理,即可验证PIL是否安装成功。






2.pytesser的使用
pytesser下载地址:链接:https://pan.baidu.com/s/1zBipW08DoT_WT-ubtIj7gw 密码:zedi
下载解压后直接放C:\Python27\Lib\site-packages(根据你安装的Python路径而不同),同时,新建一个pytesser.pth,内容就写pytesser,注意这里的内容一定要和pytesser这个文件夹同名,意思就是pytesser文件夹,pytesser.pth,及内容都要一样!







接下来打开pytesser文件夹,将pytesser.py修改成__init__.py,然后打开py文件进行修改:
1. import Image —> from PIL import Image
2. tesseract_exe_name = 'tesseract' —> tesseract_exe_name = 'Python安装路径\\Lib\\site-packges\\pytesser\\tesseract,注意双斜杠,否则可能因为转义字符报错







pytesser同时依赖tesseract-ocr,继续安装tesseract-ocr了,下载地址:链接:https://pan.baidu.com/s/1sbt4yt3y1LuPIxQVVug0AA 密码:2cb5
下载后解压,tessdata文件夹,用其替换掉pytesser解压后的tessdata文件夹即可。
3.进行实验






可以识别简单的验证码
复杂的呢?下图本来是有背景的,去除背景后识别率并非100%






有干扰线的根本无法识别






于是,不服气的我就做了一下尝试
1.收集验证码原材料






2.去除原图片噪点、将背景色处理成白色






3.将去噪点后的图片切割并人为打标签分库






然而。成功率并不高,因为干扰线的原因,数字形状不固定。
所有如果能去除干扰线,成功率就会提升一大截。
现在我还是个菜鸟,代码基础不太好,暂时无法解决,现在的方案是让开发在代码中加万能验证码。
人工智能图像识别,暂时就告一段落了,以后有新的进展会及时更新。
 
文章首发于公众号「brucepk」,欢迎直接去公众号看,定期分享自动化和python干货






  查看全部
1.jpeg

处于安全考虑,大部分的网站登录页面都会有验证码,验证码的有简单也有复杂的;
以下是度娘对验证码识别的介绍
验证码的主要目的是强制人机交互来抵御机器自动化攻击的。大部分的验证码设计者并不得要领,不了解图像处理,机器视觉,模式识别,人工智能的基本概念。
看来,验证码识别涉及到人工智能,这篇文章,通过验证码识别,对人工智能进行一个初探。
python robot框架有图像处理库PIL、pytesser、tesseract
环境搭建
1.pillow命令安装
pip install Pillow
用pip命令安装不了的话,也可以去官网下载whl包进行安装
https://pypi.org/project/Pillow/
安装完毕,需要检查一下安装是否成功。
如下图所示,打开python后,输入import Image,会提示没有这个模块。因为使用pillow库需要使用from PIL import Image代替import Image。按照图中的处理,即可验证PIL是否安装成功。

2.png


2.pytesser的使用
pytesser下载地址:链接:https://pan.baidu.com/s/1zBipW08DoT_WT-ubtIj7gw 密码:zedi
下载解压后直接放C:\Python27\Lib\site-packages(根据你安装的Python路径而不同),同时,新建一个pytesser.pth,内容就写pytesser,注意这里的内容一定要和pytesser这个文件夹同名,意思就是pytesser文件夹,pytesser.pth,及内容都要一样!

3.png



接下来打开pytesser文件夹,将pytesser.py修改成__init__.py,然后打开py文件进行修改:
1. import Image —> from PIL import Image
2. tesseract_exe_name = 'tesseract' —> tesseract_exe_name = 'Python安装路径\\Lib\\site-packges\\pytesser\\tesseract,注意双斜杠,否则可能因为转义字符报错

4.png



pytesser同时依赖tesseract-ocr,继续安装tesseract-ocr了,下载地址:链接:https://pan.baidu.com/s/1sbt4yt3y1LuPIxQVVug0AA 密码:2cb5
下载后解压,tessdata文件夹,用其替换掉pytesser解压后的tessdata文件夹即可。
3.进行实验

5.png


可以识别简单的验证码
复杂的呢?下图本来是有背景的,去除背景后识别率并非100%

6.png


有干扰线的根本无法识别

7.png


于是,不服气的我就做了一下尝试
1.收集验证码原材料

8.png


2.去除原图片噪点、将背景色处理成白色

9.png


3.将去噪点后的图片切割并人为打标签分库

10.png


然而。成功率并不高,因为干扰线的原因,数字形状不固定。
所有如果能去除干扰线,成功率就会提升一大截。
现在我还是个菜鸟,代码基础不太好,暂时无法解决,现在的方案是让开发在代码中加万能验证码。
人工智能图像识别,暂时就告一段落了,以后有新的进展会及时更新。
 
文章首发于公众号「brucepk」,欢迎直接去公众号看,定期分享自动化和python干货

11.jpg


 

robotframework成功运行case后,点击关闭按钮-无法关闭并提示SENDING

回复

雪花下的芬芳 回复了问题 • 1 人关注 • 1 个回复 • 376 次浏览 • 2018-04-26 17:35 • 来自相关话题

各位大神求推荐RF相关视频呢

虾虾吃泥巴 回复了问题 • 2 人关注 • 2 个回复 • 618 次浏览 • 2018-03-01 13:26 • 来自相关话题

robotframework 接口测试 +RSA 加密

学习真 发表了文章 • 4 个评论 • 1312 次浏览 • 2017-11-29 15:05 • 来自相关话题

首先,实现RSA加密,需要用到pycrypto这个库,这个库又依赖openssl,所以需要先下载openssl,具体教程可以参考http://bbs.csdn.net/topics/392193545?page=1
安装完成后就可以安装pycrypto, pip install pycrypto ,如果报的错是VC++ ,那么就现在安装vc++在安装pycrypto ,https://www.microsoft.com/en-us/download/details.aspx?id=44266
java版的加密函数如下:public static String sign(byte data, final String privateKey) throws Exception {
byte keyBytes = Base64Utils.decrypt(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(privateK);
signature.update(data);
return Base64Utils.encrypt(signature.sign());
}具体的python函数如下:import hashlib
import base64
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA
from Crypto.Hash import MD5

priKey = '''-----BEGIN RSA PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAN8M7oBSoZOzAoxL3tmzku/ZTtQn/BBqfe8jj0GZeFKh0IY8qDpFrNONzxp4S+TH4xCXYyEFkkEIcS9SKMCbqba
-----END RSA PRIVATE KEY-----'''

def sign(self, signdate):
reload(sys)
sys.setdefaultencoding('utf-8')
h=MD5.new(signdate)
signer = PKCS1_v1_5.new(RSA.importKey(priKey))
signn = signer.sign(h)
signn=base64.urlsafe_b64encode(signn)
return signnpriKey是私钥,每个公司的私钥是不一样的。

在robotframework中导入自己写的py文件就可以调用sign了,但是这个签名结果后面多了一个‘=’,需要加一步替换,把=替换为空,





在python内置的base64库中,可以直接进行编码,base64.b64encode,但是编码后的数据可能会出现‘+’或者‘/’,这在rul中是不能作为参数的,而base64.rulsafe_b64encode则把‘+’或‘/’转换成‘-‘或者’_‘。具体编码函数看需求,如果是做url则必须用urlsafe
Base64是一种通过查表的编码方法,不能用于加密,即使使用自定义的编码表也不行。
Base64适用于小段内容的编码,比如数字证书签名、Cookie的内容等。
由于=字符也可能出现在Base64编码中,但=用在URL、Cookie里面会造成歧义,所以,很多Base64编码后会把=去掉,这也就是上面提到的为什么多了一个’=‘。

get。
接口测试第一步是创建session,第一个参数是alias,也就是命名,识别用的,第二个参数为url,第二行为rsa加密,看需要,第五行是创建头文件(具体参数看公司需求),第六行是参数变量,第八行即为连接api,第一个参数是alias,需要跟上面的保持一致,第二个参数是uri,第三第四为头文件跟参数变量





 
post
post跟get基本一致,在post需要注意接口传参是用data还是用params。params是在请求url里的参数,而data是请求body里的,可以是json,也可以是urlencoded,试具体情况而定(经雪霁大神指正)。





data如下,其实也就是把params改成data





 
使用params还是data对签名也有影响,使用params时,签名需要把所有参数附上,而且得按字母排序,用data时就不用加上参数。(本公司RSA加密的签名规则是这样,其他公司的具体的就不知道了,不懂得可以问问公司开发人员)具体如下:/public/corporate/fund/proposa?fundCode=${fundCode}&investmentAmount=${investmentAmount}&x-api-key=I&x-api-timestamp=${timestamp}&x-api-version=2.0
/public/corporate/fund?x-api-key=I&x-api-timestamp=${timestamp}&x-api-version=2.0在我这里,rsa加密,如果参数是json格式,那么在加密的时候是不需要加参数的,参数直接在post request用data进行传输,数据在这里有一个需要注意的地方,那就是{
"accountNumber": "IF2016070100000044",
"corporateUserCode": 0,
"investorPayId": 28,
"merchantNumber": "IF2017112100003",
"password": "if123",
"payMethod": 0,
"purchaseFunds": [
{
"currency": "156",
"fundCode": "0408",
"investmentAmount": 10
}
],
"riskConfirmed": 1
}json里面有没有出现数组,即有没有出现中括号’[’,‘ ]‘,有的话,需要先进行create dictionary 之后再进行create list,这样传输的数据才能正确。

时间戳:
获取当前时间的时间戳为:${date} get time epoch;get time 是内置库BuiltIn的关键字
把时间转成时间戳:${date} Convert Date 2017-11-22 10:00:00 epoch;Convert Date是Date Time库的关键字
把时间戳转成时间:${date} get time ‘空一格’ ${time};注意在空一格那位置上空一格。。
 
有什么错误的地方欢迎大家指点,共同学习。 查看全部
首先,实现RSA加密,需要用到pycrypto这个库,这个库又依赖openssl,所以需要先下载openssl,具体教程可以参考http://bbs.csdn.net/topics/392193545?page=1
安装完成后就可以安装pycrypto, pip install pycrypto ,如果报的错是VC++ ,那么就现在安装vc++在安装pycrypto ,https://www.microsoft.com/en-us/download/details.aspx?id=44266
java版的加密函数如下:
public static String sign(byte data, final String privateKey) throws Exception {
byte keyBytes = Base64Utils.decrypt(privateKey);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(privateK);
signature.update(data);
return Base64Utils.encrypt(signature.sign());
}
具体的python函数如下:
import hashlib
import base64
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA
from Crypto.Hash import MD5

priKey = '''-----BEGIN RSA PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAN8M7oBSoZOzAoxL3tmzku/ZTtQn/BBqfe8jj0GZeFKh0IY8qDpFrNONzxp4S+TH4xCXYyEFkkEIcS9SKMCbqba
-----END RSA PRIVATE KEY-----'''

def sign(self, signdate):
reload(sys)
sys.setdefaultencoding('utf-8')
h=MD5.new(signdate)
signer = PKCS1_v1_5.new(RSA.importKey(priKey))
signn = signer.sign(h)
signn=base64.urlsafe_b64encode(signn)
return signn
priKey是私钥,每个公司的私钥是不一样的。

在robotframework中导入自己写的py文件就可以调用sign了,但是这个签名结果后面多了一个‘=’,需要加一步替换,把=替换为空,

替换.PNG

在python内置的base64库中,可以直接进行编码,base64.b64encode,但是编码后的数据可能会出现‘+’或者‘/’,这在rul中是不能作为参数的,而base64.rulsafe_b64encode则把‘+’或‘/’转换成‘-‘或者’_‘。具体编码函数看需求,如果是做url则必须用urlsafe
Base64是一种通过查表的编码方法,不能用于加密,即使使用自定义的编码表也不行。
Base64适用于小段内容的编码,比如数字证书签名、Cookie的内容等。
由于=字符也可能出现在Base64编码中,但=用在URL、Cookie里面会造成歧义,所以,很多Base64编码后会把=去掉,这也就是上面提到的为什么多了一个’=‘。

get。
接口测试第一步是创建session,第一个参数是alias,也就是命名,识别用的,第二个参数为url,第二行为rsa加密,看需要,第五行是创建头文件(具体参数看公司需求),第六行是参数变量,第八行即为连接api,第一个参数是alias,需要跟上面的保持一致,第二个参数是uri,第三第四为头文件跟参数变量

get.PNG

 
post
post跟get基本一致,在post需要注意接口传参是用data还是用params。params是在请求url里的参数,而data是请求body里的,可以是json,也可以是urlencoded,试具体情况而定(经雪霁大神指正)。
pos.PNG


data如下,其实也就是把params改成data

post——data.png

 
使用params还是data对签名也有影响,使用params时,签名需要把所有参数附上,而且得按字母排序,用data时就不用加上参数。(本公司RSA加密的签名规则是这样,其他公司的具体的就不知道了,不懂得可以问问公司开发人员)具体如下:
/public/corporate/fund/proposa?fundCode=${fundCode}&investmentAmount=${investmentAmount}&x-api-key=I&x-api-timestamp=${timestamp}&x-api-version=2.0
/public/corporate/fund?x-api-key=I&x-api-timestamp=${timestamp}&x-api-version=2.0
在我这里,rsa加密,如果参数是json格式,那么在加密的时候是不需要加参数的,参数直接在post request用data进行传输,数据在这里有一个需要注意的地方,那就是
{
"accountNumber": "IF2016070100000044",
"corporateUserCode": 0,
"investorPayId": 28,
"merchantNumber": "IF2017112100003",
"password": "if123",
"payMethod": 0,
"purchaseFunds": [
{
"currency": "156",
"fundCode": "0408",
"investmentAmount": 10
}
],
"riskConfirmed": 1
}
json里面有没有出现数组,即有没有出现中括号’[’,‘ ]‘,有的话,需要先进行create dictionary 之后再进行create list,这样传输的数据才能正确。

时间戳:
获取当前时间的时间戳为:${date} get time epoch;get time 是内置库BuiltIn的关键字
把时间转成时间戳:${date} Convert Date 2017-11-22 10:00:00 epoch;Convert Date是Date Time库的关键字
把时间戳转成时间:${date} get time ‘空一格’ ${time};注意在空一格那位置上空一格。。
 
有什么错误的地方欢迎大家指点,共同学习。

rf装逼神器--evaluate

雪霁 发表了文章 • 6 个评论 • 3596 次浏览 • 2017-11-10 16:26 • 来自相关话题

装逼之前先稍微科普一下:
Robotframework 中的evaluate关键字底层的代码使用的是python的eval函数,
eval语句用来计算存储在代码对象或字符串中的有效的Python表达式,eval函数的语法:eval(source[, globals[, locals]]) -> value


eval函数是用来动态地执行一个表达式的字符串,或者compile函数编译出来的代码对象。参数source是一个表达式字符串,或者表示编译出来代码对象的名称;参数globals是全局命名空间,可以指定执行表达式时的全局作用域的范围,比如指定某些模块可以使用。如果本参数缺省,就使用当前调用这个函数的当前全局命名空间;参数locals是局部作用域命名空间,是用来指定执行表达式时访问的局部命名空间。如果全局命名空间参数出现,但缺省内置模块,那么会自动拷贝这个模块到全局命名空间,意味着无论怎么设置,都可以使用内置模块。如果两个命名空间,都使用缺省方式,就会使用调用这个函数时的命名空间来查找相应的变量。
 
在Evaluate关键字,对外只暴露了入参source和globals,看源码locals留给rf内部变量了,2.9以后表达式‘$variable’变得屌屌的。
 
概念有点难理解。。。瞬间感觉这个逼要装不下去了,还是一起看下面的具体使用技巧吧:
1、转换和计算
1.1  将字符串表达式转为list,dict,tuple









 
1.2 复杂字符串转成你想要的json的样子









 
1.3  简单的数学运算









 
1.4 python函数式编程等









 
1.5 复杂一点的带多个命名空间的运算









 
 
注意事项,字符串中有’\n’会转换失败,转换前需先去掉!!!!
 
2、使用python的builtin函数
2.1 获取、判断数据类型

这个在上面的例子中已经有了,放这主要讲一下原理哈${str} set variable 123
${type} evaluate type($str)由于带花括号时,rf的evaluate的运算机制是:
第1步 将最后一格的表达式替换,用的是直接字符串相加的方法,替换的结果是: ‘type(123)’,没毛病
第2步,eval运算,type(123)当然是int类型的啦,真是简单粗暴耿直

然后我们来看一下不带花括号的:
第一步,将字符串切了,比如例子中的,会切成 type,(,$,str,)
第二步,找到$及紧随其后的那个元素,就是str,然后在rf的命名空间里找这个的变量,要是找到了,就把它标记为 RF_VAR_str,同时一并把其真实值也对应上
第三步,进行eval运算,这个运算就是很正常的运算了,运算出的结果和python算出来是一样一样的,type($str)为unicode(注意,如没做特殊处理,rf格子里的变量读出来都是unicode类型)
 
2.2 求round, json.loads or json.dumps等,具体例子见附件
 

2.3 表达式判断,各种if
讲Rf的evaluate,就不得不引申到条件判断的关键字,即带if的关键字,如:run keyword if,set variable if, pass execution if...这是由于if后面跟的表达式都由evaluate操刀,因此,我们可以做python的任意一种判断。同理断言关键字should be true也可以这么玩,底层也是用的evaluate。
关于这个判断,很多人都跳过坑,其根本原因,就是evaluate先替换后运算的机制。


最后,记住一点,就是,凡是想要变量保持原有的变量类型的,都使用$variable的形式,其他的用${variable}就可以了。
 
道长有一篇博客专门讲这个,我这里主要是做一些补充,另外2.9版本后新表达式真的非常灵活,弥补了之前evaluate表达式的诸多短板。例子大部分都是做接口测试时的实例方法,接口测试的日常就是dict、list、tuple和string,evaluate和Collectoins库搭配起来用不能更趁手。
 
附件有精选的例子,可以下载运行找下感觉

  查看全部
装逼之前先稍微科普一下:
Robotframework 中的evaluate关键字底层的代码使用的是python的eval函数,
eval语句用来计算存储在代码对象或字符串中的有效的Python表达式,eval函数的语法:
eval(source[, globals[, locals]]) -> value


eval函数是用来动态地执行一个表达式的字符串,或者compile函数编译出来的代码对象。参数source是一个表达式字符串,或者表示编译出来代码对象的名称;参数globals是全局命名空间,可以指定执行表达式时的全局作用域的范围,比如指定某些模块可以使用。如果本参数缺省,就使用当前调用这个函数的当前全局命名空间;参数locals是局部作用域命名空间,是用来指定执行表达式时访问的局部命名空间。如果全局命名空间参数出现,但缺省内置模块,那么会自动拷贝这个模块到全局命名空间,意味着无论怎么设置,都可以使用内置模块。如果两个命名空间,都使用缺省方式,就会使用调用这个函数时的命名空间来查找相应的变量。
 
在Evaluate关键字,对外只暴露了入参source和globals,看源码locals留给rf内部变量了,2.9以后表达式‘$variable’变得屌屌的
 
概念有点难理解。。。瞬间感觉这个逼要装不下去了,还是一起看下面的具体使用技巧吧:
1、转换和计算
1.1  将字符串表达式转为list,dict,tuple

转listdict.png

转list_result.png

 
1.2 复杂字符串转成你想要的json的样子

复杂json.png

复杂json_result.png

 
1.3  简单的数学运算

calc.png

calc_result.png

 
1.4 python函数式编程等

函数式编程.png

函数式_result.png

 
1.5 复杂一点的带多个命名空间的运算

random.png

random_result.png

 
 
注意事项,字符串中有’\n’会转换失败,转换前需先去掉!!!!
 
2、使用python的builtin函数
2.1 获取、判断数据类型

这个在上面的例子中已经有了,放这主要讲一下原理哈
${str} set variable 123
${type} evaluate type($str)
由于带花括号时,rf的evaluate的运算机制是:
第1步 将最后一格的表达式替换,用的是直接字符串相加的方法,替换的结果是: ‘type(123)’,没毛病
第2步,eval运算,type(123)当然是int类型的啦,真是简单粗暴耿直

然后我们来看一下不带花括号的:
第一步,将字符串切了,比如例子中的,会切成 type,(,$,str,)
第二步,找到$及紧随其后的那个元素,就是str,然后在rf的命名空间里找这个的变量,要是找到了,就把它标记为 RF_VAR_str,同时一并把其真实值也对应上
第三步,进行eval运算,这个运算就是很正常的运算了,运算出的结果和python算出来是一样一样的,type($str)为unicode(注意,如没做特殊处理,rf格子里的变量读出来都是unicode类型)

 
2.2 求round, json.loads or json.dumps等,具体例子见附件
 

2.3 表达式判断,各种if
讲Rf的evaluate,就不得不引申到条件判断的关键字,即带if的关键字,如:run keyword if,set variable if, pass execution if...这是由于if后面跟的表达式都由evaluate操刀,因此,我们可以做python的任意一种判断。同理断言关键字should be true也可以这么玩,底层也是用的evaluate。
关于这个判断,很多人都跳过坑,其根本原因,就是evaluate先替换后运算的机制。


最后,记住一点,就是,凡是想要变量保持原有的变量类型的,都使用$variable的形式,其他的用${variable}就可以了。
 
道长有一篇博客专门讲这个,我这里主要是做一些补充,另外2.9版本后新表达式真的非常灵活,弥补了之前evaluate表达式的诸多短板。例子大部分都是做接口测试时的实例方法,接口测试的日常就是dict、list、tuple和string,evaluate和Collectoins库搭配起来用不能更趁手。
 
附件有精选的例子,可以下载运行找下感觉

 

robotframework-如何切换到新窗口

雪霁 发表了文章 • 8 个评论 • 3675 次浏览 • 2017-11-06 18:47 • 来自相关话题

前提:
我们说的新窗口,统一指下图这样的窗口,即点击一个链接,产生一个新的tab页,需要到新的窗口定位元素。请各位对号入座,看清楚再服用此文章:





知识点:
这大概就是大部分人的选择性失明 及 越长越只会看图不会看字 的结果,F5查询select window用法这一条说明写在这里2年多了,九成的人表示没看到 。建议使用2.9以上的rf版本,及最新的ride版本。





 
实现:





 
 
PS: 本来不想一个关键字写一篇分享的,问的人越来越多,而且回答了用select window | new 还一句话堵不死的,后面还要他跟诸多解释为什么能这么用。So...... 查看全部
前提:
我们说的新窗口,统一指下图这样的窗口,即点击一个链接,产生一个新的tab页,需要到新的窗口定位元素。请各位对号入座,看清楚再服用此文章:

7.png

知识点:
这大概就是大部分人的选择性失明越长越只会看图不会看字 的结果,F5查询select window用法这一条说明写在这里2年多了,九成的人表示没看到 。建议使用2.9以上的rf版本,及最新的ride版本。

2.png

 
实现:

3.png

 
 
PS: 本来不想一个关键字写一篇分享的,问的人越来越多,而且回答了用select window | new 还一句话堵不死的,后面还要他跟诸多解释为什么能这么用。So......

rf变量进阶--变量文件的使用,环境快捷切换等

雪霁 发表了文章 • 4 个评论 • 2363 次浏览 • 2017-10-21 17:29 • 来自相关话题

本文解决三个问题

1、变量文件的使用
2、环境相关变量的快捷切换
3、依赖变量文件的全局变量,可在用例中更改值,给后续用例使用

相信大家和我一样,自动化测试用例一般不止运行在测试环境,通常情况下是三套环境:测试,预发布及生产环境。调试或者辅助验证测试时,切环境改变量甚是麻烦。这些变量包括但不限于:一些url信息,数据库信息,预置用户信息。

so,翻了一大遍用户手册,最后决定通过判断运行时输入的变量来返回不同的环境信息,如图所示:





然后,下面是导入Variables 视图,导入变量文件时使用args,传入变量${env},实际值为运行时为上图指定的变量值:test。





 
以下是comVars.py的内容:
注意get_variables()函数,这个是rf指定专用返回变量的函数,示例中定义的变量有 ${mainurl},${db_trade},${db_trade_pw} 及${globalvars},导入后运行时可以直接使用,等同于在suite文件或resources文件里定义的变量# -*- coding: utf-8 -*-

def get_variables(env = 'test'):
if env=='product':
#production environment
variables = {
#urlConfig
"mainurl" : "http://main.com",
#dbConfig
"db_trade" : "tradeXXX",
"db_trade_pw" : "12345678",
}

elif env=='exp':
#staging environment
variables = {
#urlConfig
"mainurl" : "http://exp.main.com",
#dbConfig
"db_trade" : "trade_exp_XXX",
"db_trade_pw" : "12345678",
}

else:
#text environment
variables = {
#urlConfig
"mainurl" : "http://test.main.com",
#dbConfig
"db_trade" : "trade_test_XXX",
"db_trade_pw" : "12345678",
}
globalvars = {'productid':'3456','userid':'test12'} #共用变量
variables['globalvars'] = globalvars

return variables这类变量文件还有更多好玩的用法,如构造某个函数动态得出值等,相对于用rf里的scalar会灵活一些,更多惊喜的用法,大家可以自己去探索。


对于上述需求,其实还有另一种选择,即在运行Arguments里填写-V参数,导入不同的variables文件,如把测试环境的所有初始变量写到testvars.py,生产的写的productionvars.py。这种方法应该更常见一些,各人各有喜好吧,我更偏向上一种些。初始化参数体量比较小时,维护一个文件比较方便。





 
然后testvars.py就长这样:# -*- coding: utf-8 -*-

#urlConfig
mainurl = 'http://test.main.com'

#dbConfig
db_trade = 'trade_test_XXX'
db_trade_pw = '1234567'

#otherVars
globalvars = {'productid':'3456','userid':'test12'}
最后,我们来探讨一下,所谓的rf的全局变量,想要在用例里改变它的值,并且想在后续用例里拿它改变后的值,的一种较迂回曲折的做法。思路:通过变量文件,利用字典update方法将改变内存里的值的特性。

如初始在文件变量里定义了一个变量userid为12,建议做法是:把它放在一个字典里,字典叫什么名字都可以,如
     globalvars = {'productid':'3456','userid':'test12'}
 
使用时,导入该变量文件,取值时用${globalvars['userid']}取,更新时用update更新,如图,图中两个suite 文件Test2和Test3都导入了该变量文件,在Test2下的case1中更新该变量,在Test3的case3下,它的值还是test13,再一次用update的方法更新时,后续所有的值都会是更新后的值。如此,达到用例中间变更变量后续可用的目的。用evaluate  update时,注意变量的写法,大括号建议都不写。










 
日志打印:






 
以上所有文件,均放在附件压缩包里,可直接运行查看效果
  查看全部
本文解决三个问题

1、变量文件的使用
2、环境相关变量的快捷切换
3、依赖变量文件的全局变量,可在用例中更改值,给后续用例使用


相信大家和我一样,自动化测试用例一般不止运行在测试环境,通常情况下是三套环境:测试,预发布及生产环境。调试或者辅助验证测试时,切环境改变量甚是麻烦。这些变量包括但不限于:一些url信息,数据库信息,预置用户信息。

so,翻了一大遍用户手册,最后决定通过判断运行时输入的变量来返回不同的环境信息,如图所示:

p1.png

然后,下面是导入Variables 视图,导入变量文件时使用args,传入变量${env},实际值为运行时为上图指定的变量值:test。

p2.png

 
以下是comVars.py的内容:
注意get_variables()函数,这个是rf指定专用返回变量的函数,示例中定义的变量有 ${mainurl},${db_trade},${db_trade_pw} 及${globalvars},导入后运行时可以直接使用,等同于在suite文件或resources文件里定义的变量
# -*- coding: utf-8 -*-

def get_variables(env = 'test'):
if env=='product':
#production environment
variables = {
#urlConfig
"mainurl" : "http://main.com",
#dbConfig
"db_trade" : "tradeXXX",
"db_trade_pw" : "12345678",
}

elif env=='exp':
#staging environment
variables = {
#urlConfig
"mainurl" : "http://exp.main.com",
#dbConfig
"db_trade" : "trade_exp_XXX",
"db_trade_pw" : "12345678",
}

else:
#text environment
variables = {
#urlConfig
"mainurl" : "http://test.main.com",
#dbConfig
"db_trade" : "trade_test_XXX",
"db_trade_pw" : "12345678",
}
globalvars = {'productid':'3456','userid':'test12'} #共用变量
variables['globalvars'] = globalvars

return variables
这类变量文件还有更多好玩的用法,如构造某个函数动态得出值等,相对于用rf里的scalar会灵活一些,更多惊喜的用法,大家可以自己去探索。


对于上述需求,其实还有另一种选择,即在运行Arguments里填写-V参数,导入不同的variables文件,如把测试环境的所有初始变量写到testvars.py,生产的写的productionvars.py。这种方法应该更常见一些,各人各有喜好吧,我更偏向上一种些。初始化参数体量比较小时,维护一个文件比较方便。

p3.png

 
然后testvars.py就长这样:
# -*- coding: utf-8 -*-

#urlConfig
mainurl = 'http://test.main.com'

#dbConfig
db_trade = 'trade_test_XXX'
db_trade_pw = '1234567'

#otherVars
globalvars = {'productid':'3456','userid':'test12'}

最后,我们来探讨一下,所谓的rf的全局变量,想要在用例里改变它的值,并且想在后续用例里拿它改变后的值,的一种较迂回曲折的做法。思路:通过变量文件,利用字典update方法将改变内存里的值的特性。

如初始在文件变量里定义了一个变量userid为12,建议做法是:把它放在一个字典里,字典叫什么名字都可以,如
     globalvars = {'productid':'3456','userid':'test12'}
 
使用时,导入该变量文件,取值时用${globalvars['userid']}取,更新时用update更新,如图,图中两个suite 文件Test2和Test3都导入了该变量文件,在Test2下的case1中更新该变量,在Test3的case3下,它的值还是test13,再一次用update的方法更新时,后续所有的值都会是更新后的值。如此,达到用例中间变更变量后续可用的目的。用evaluate  update时,注意变量的写法,大括号建议都不写。

p4.png


p5.png

 
日志打印:

p7.png


 
以上所有文件,均放在附件压缩包里,可直接运行查看效果
 

robotframework用例级别的并行执行

雪霁 发表了文章 • 5 个评论 • 4660 次浏览 • 2017-09-19 09:47 • 来自相关话题

一、需求

这篇分享主要解决的是@Hurts 同学的痛点。一开始我觉得这不是个痛点,可能很多同学也会认为这是个伪需求,suite的并行运行,git上有个pabot可以直接使用。而这篇文章分享的主题是针对suite内用例级的并行运行,用例以suite为单位已经成了我的一个认知性思维:suite内用例不需要相互独立,允许耦合,允许依赖,顺序执行,共用suite setup。我自己写的用例,考虑到少写重复代码的原因,也默认有些依赖关系。

但有些团队不一样,同一suite下,用例是相互独立的,有利也有弊吧,都是基于不同考虑。Hurts的痛点是,单个用例运行时间特别长(常见于电商,贷款,OA等流程性页面测试),运行环境资源壕到没话说,由于不同suite的用例数不一致,加上用例运行时间有长有短,suite级的并行无法达到最优的效率。而通过下面的脚本,结合Grid + Docker,运行效率提升特别明显,至于提升数据,应该是好十几倍吧,下次让他拿数据来说话,这里只介绍用例级并行脚本的实现。
 
二、思路

第一步,分解用例:

因为我对robot的源码有一些些了解,所以知道robot/run.py是pybot的运行入口,然后发现类 RobotFramework里的main函数里,有个suite实例的返回,这就是我们的突破口(如果完全没看过源码的同学怎么办,猜嘛,那个字看着就像啊,哈哈)。rf里其实以suite为最小单位运行,而我们想要的是更细分的用例。查看源码可以知道,suite实例有两个很重要的属性是我们要用到的,1、tests(suite下的tests)  2、suites(suite下的suites)。通过这两个属性,就可以把suite分成单个单个的了。这里用了一个递归。
def _split_tests(self, suite, testlong):
if suite.suites:
for suites in suite.suites:
self._split_tests(suites,testlong)
else:
for test in suite.tests:
print test.longname
testnames = test.longname.decode()
testlong.append(testnames)
return testlong
 
第二步,并行运行分解后的用例:

分成了各个Test以后,该怎么并行运行?答案就是,多线程/进程执行pybot,就评估来看,这种方法是改动最小的,如果要深入到robot里面,将原来的suite.run逻辑改成test.run的逻辑,那就有些复杂了,这样会动到底层robot的代码,那权衡之下,robot还是原来的robot,不做任何改动,并行代码底层依赖pybot,并用多线程/进程启动多个pybot执行,rf脚本也还是原来的脚本,不需要做任何改动。另外,pybot执行单个用例用-t参数。 def parallel_run(self,testnames, logFolder,data_sources,extra_options_cmd='',processnum = 1):
starttime = time.time()
print 'start at: ', time.ctime()

commands_list = []
for tests in testnames:
output = "%s/%s_Output.xml" % (logFolder,tests)
PybotCommand = 'pybot ' + extra_options_cmd + '-t "'+ tests + '" -o "' + output +'" ' + data_sources
print(PybotCommand)
commands_list.append(PybotCommand)

pool = ProcessPool(processnum) #多进程方式并行
results = pool.map(os.system,commands_list)
pool.close()
pool.join()

time.sleep(1) #报告生成需要点时间,sleep一下下
endtime = time.time()
print 'end at: ', time.ctime()
print 'elapsed time:', endtime-starttime 
 
第三步,报告合并

用rebot命令就可以合并报告,在脚本里,使用了一个subprocess,启动cmd命令。当然,前提是,生成了一堆比较有命名规律的xml,这里用的是XXX_Output.xml
rebotCommand = 'rebot --outputdir "' + logFolder + '" --merge "' + logFolder + '/*_Output.xml"'
print(rebotCommand)
merge_proc = subprocess.Popen(rebotCommand, shell=True)
merge_proc.communicate()
 
第四步,一点小清理工作

养成好习惯,实际运行前清环境。。。不然,合并报告时容易报错T_T。。。
def clear_env(self, logFolder):
for files in os.listdir(logFolder):
if files.endswith('_Output.xml') or files.endswith('.png'):
os.remove(logFolder +'/' + files)
 
 
到此,解决基本功能问题,然后就是拓展问题

有一个无法避免的问题是:pybot执行可选命令参数,我常用到的有,-v -i -e -d 等,然而有一些参数并不适合并行测试时使用,然后我根据个人习惯拓展了几个常用options。options这些实现,完全可以套用robot里面现成的一套逻辑,不必自己再大费周章写代码解析了。套用后,进入到主程序,rf读出来的参数可能已经不是我们想要的样子,需要再用个方法逆向处理一下,才能再给pybot用:
def unresolve_options(self,options):
extra_options_cmd = ''
_extra_options = ['variable',
'variablefile',
'outputdir'] #如需再拓展其他tag无关的参数,需在这加上
for key,value in options.items():
if key not in _extra_options:
options.pop(key)
elif isinstance(value,unicode):
extra_options_cmd = extra_options_cmd + '--' + key + ' ' + value + ' '
elif value != []:
for action in value:
extra_options_cmd = extra_options_cmd + '--' + key + ' ' + action + ' '
return extra_options_cmd
 
另外,说明文档的USAGE,也就是那一大段用三引号圈起来的说明必不可少,它直接关系到可选参数能不能用。如果需要拓展其他的参数,也需在USAGE里加上,前提是pybot也支持这些参数。
 
 
三、实现

以下是完整的代码,附件可供下载(本站不支持py附件,得贴个git地址)
 
 
 
四、使用

 
最基本的使用:建议robotframework 版本 3.0.2 及以上

python Parabot.py /Users/XXXXX/RobotSub

带参数执行(指定输出目录./logs,包含tag 'parallel',20个进程,初始化变量env为test):

python Parabot.py -d ./logs -i parallel -p 20 -v env:test  /Users/XXXXX/RobotSub


添加其他可选参数:

已支持的可选参数用--help可以看

另外想要加的,比如说自己拓展的--retry 参数,需要改该文件的两个地方:

1、USAGE的options里增加一行,(加粗行):
Options
=======

[b] -r --retry retry Max time to retry
[/b] -d --outputdir dir Where to create output files. The default is the
directory where tests are run from and the given path
is considered relative to that unless it is absolute.
 
2、unresolve_options方法里的_extra_options列表,增加一个元素(加粗行)
_extra_options = ['variable',
‘variablefile',
[b]‘retry’[/b],
'outputdir']
 
五、解决的一些小问题

1、线程池,进程池,还是并行subprocess方式?

    一开始采用的是直接多个的subprocess的形式,发现控制不好力度,一口气都起来了,部分进程等着等着就超时了。后来在单台机器上试线程池表现也不错,毕竟是IO密集型,上grid后发现多个线程起的driver实例都在一个node上面,而隔离性不是很好,然后会有些奇怪的报错。最后改用进程池方式,一个node一个driver,有点耗资源,却是针对此种情景的最优解。


2、错误截图都自个覆盖了怎么办?

对rf来说,每个新启动的pybot的进程都独立无交集,截图都从selenium-screenshot-1.png开始命名,同时运行时,就会被覆盖掉,日志里就找不到了。

本着能不改rf源码就不改的原则,最后还是改了两行,加上import 语句三行
Selenium2Library版本号 1.8.0,修改文件 Selenium2Library/keywords/_screenshot.py
顶行增加:
import random, string
capture_page_screenshot方法里,在原来的代码前增加两行:
_rand_str = ''.join([random.choice(string.digits+string.letters) for i in range(6)])
filename = _rand_str + '-' + filename

 
六、还可以优化的地方

1、可以再改改,用ride也可以直接运行该脚本,前提是调试环境也足够壕

2、多data source日志合并会出错,所以暂不支持命令里写几个suite的,因为日志合并需要写很多很多的代码

3、有些不常用的pybot可选参数没有支持,可能支持也不会太好。有些并不需要支持

4、由于多个进程同时运行,message的打印,就有点临乱。。。

5、键盘中断运行,没有做。。。。


重要提醒:本文只针对用例级别的并行,也就是说,保证用例间独立是前提。suite级别的并行运行请出门拐至:https://github.com/mkorpela/pabot 。这家伙也是好久好久了才更新一次,所采用方法也基本无差别,他的用的线程池方式,不过好像启多个driver运行效果很不理想。
  查看全部
一、需求

这篇分享主要解决的是@Hurts 同学的痛点。一开始我觉得这不是个痛点,可能很多同学也会认为这是个伪需求,suite的并行运行,git上有个pabot可以直接使用。而这篇文章分享的主题是针对suite内用例级的并行运行,用例以suite为单位已经成了我的一个认知性思维:suite内用例不需要相互独立,允许耦合,允许依赖,顺序执行,共用suite setup。我自己写的用例,考虑到少写重复代码的原因,也默认有些依赖关系。

但有些团队不一样,同一suite下,用例是相互独立的,有利也有弊吧,都是基于不同考虑。Hurts的痛点是,单个用例运行时间特别长(常见于电商,贷款,OA等流程性页面测试),运行环境资源壕到没话说,由于不同suite的用例数不一致,加上用例运行时间有长有短,suite级的并行无法达到最优的效率。而通过下面的脚本,结合Grid + Docker,运行效率提升特别明显,至于提升数据,应该是好十几倍吧,下次让他拿数据来说话,这里只介绍用例级并行脚本的实现。
 
二、思路

第一步,分解用例:

因为我对robot的源码有一些些了解,所以知道robot/run.py是pybot的运行入口,然后发现类 RobotFramework里的main函数里,有个suite实例的返回,这就是我们的突破口(如果完全没看过源码的同学怎么办,猜嘛,那个字看着就像啊,哈哈)。rf里其实以suite为最小单位运行,而我们想要的是更细分的用例。查看源码可以知道,suite实例有两个很重要的属性是我们要用到的,1、tests(suite下的tests)  2、suites(suite下的suites)。通过这两个属性,就可以把suite分成单个单个的了。这里用了一个递归。
    def _split_tests(self, suite, testlong):
if suite.suites:
for suites in suite.suites:
self._split_tests(suites,testlong)
else:
for test in suite.tests:
print test.longname
testnames = test.longname.decode()
testlong.append(testnames)
return testlong

 
第二步,并行运行分解后的用例:

分成了各个Test以后,该怎么并行运行?答案就是,多线程/进程执行pybot,就评估来看,这种方法是改动最小的,如果要深入到robot里面,将原来的suite.run逻辑改成test.run的逻辑,那就有些复杂了,这样会动到底层robot的代码,那权衡之下,robot还是原来的robot,不做任何改动,并行代码底层依赖pybot,并用多线程/进程启动多个pybot执行,rf脚本也还是原来的脚本,不需要做任何改动。另外,pybot执行单个用例用-t参数。
    def parallel_run(self,testnames, logFolder,data_sources,extra_options_cmd='',processnum = 1):
starttime = time.time()
print 'start at: ', time.ctime()

commands_list = []
for tests in testnames:
output = "%s/%s_Output.xml" % (logFolder,tests)
PybotCommand = 'pybot ' + extra_options_cmd + '-t "'+ tests + '" -o "' + output +'" ' + data_sources
print(PybotCommand)
commands_list.append(PybotCommand)

pool = ProcessPool(processnum) #多进程方式并行
results = pool.map(os.system,commands_list)
pool.close()
pool.join()

time.sleep(1) #报告生成需要点时间,sleep一下下
endtime = time.time()
print 'end at: ', time.ctime()
print 'elapsed time:', endtime-starttime
 
 
第三步,报告合并

用rebot命令就可以合并报告,在脚本里,使用了一个subprocess,启动cmd命令。当然,前提是,生成了一堆比较有命名规律的xml,这里用的是XXX_Output.xml
        rebotCommand = 'rebot --outputdir "' + logFolder + '" --merge "' + logFolder + '/*_Output.xml"'
print(rebotCommand)
merge_proc = subprocess.Popen(rebotCommand, shell=True)
merge_proc.communicate()

 
第四步,一点小清理工作

养成好习惯,实际运行前清环境。。。不然,合并报告时容易报错T_T。。。
    def clear_env(self, logFolder):
for files in os.listdir(logFolder):
if files.endswith('_Output.xml') or files.endswith('.png'):
os.remove(logFolder +'/' + files)

 
 
到此,解决基本功能问题,然后就是拓展问题

有一个无法避免的问题是:pybot执行可选命令参数,我常用到的有,-v -i -e -d 等,然而有一些参数并不适合并行测试时使用,然后我根据个人习惯拓展了几个常用options。options这些实现,完全可以套用robot里面现成的一套逻辑,不必自己再大费周章写代码解析了。套用后,进入到主程序,rf读出来的参数可能已经不是我们想要的样子,需要再用个方法逆向处理一下,才能再给pybot用:
    def unresolve_options(self,options):
extra_options_cmd = ''
_extra_options = ['variable',
'variablefile',
'outputdir'] #如需再拓展其他tag无关的参数,需在这加上
for key,value in options.items():
if key not in _extra_options:
options.pop(key)
elif isinstance(value,unicode):
extra_options_cmd = extra_options_cmd + '--' + key + ' ' + value + ' '
elif value != []:
for action in value:
extra_options_cmd = extra_options_cmd + '--' + key + ' ' + action + ' '
return extra_options_cmd

 
另外,说明文档的USAGE,也就是那一大段用三引号圈起来的说明必不可少,它直接关系到可选参数能不能用。如果需要拓展其他的参数,也需在USAGE里加上,前提是pybot也支持这些参数。
 
 
三、实现

以下是完整的代码,附件可供下载(本站不支持py附件,得贴个git地址)
 
 
 
四、使用

 
最基本的使用:建议robotframework 版本 3.0.2 及以上

python Parabot.py /Users/XXXXX/RobotSub

带参数执行(指定输出目录./logs,包含tag 'parallel',20个进程,初始化变量env为test):

python Parabot.py -d ./logs -i parallel -p 20 -v env:test  /Users/XXXXX/RobotSub


添加其他可选参数:

已支持的可选参数用--help可以看

另外想要加的,比如说自己拓展的--retry 参数,需要改该文件的两个地方:

1、USAGE的options里增加一行,(加粗行):
Options
=======

[b] -r --retry retry Max time to retry
[/b] -d --outputdir dir Where to create output files. The default is the
directory where tests are run from and the given path
is considered relative to that unless it is absolute.

 
2、unresolve_options方法里的_extra_options列表,增加一个元素(加粗行)
       _extra_options = ['variable',
‘variablefile',
[b]‘retry’[/b],
'outputdir']

 
五、解决的一些小问题

1、线程池,进程池,还是并行subprocess方式?

    一开始采用的是直接多个的subprocess的形式,发现控制不好力度,一口气都起来了,部分进程等着等着就超时了。后来在单台机器上试线程池表现也不错,毕竟是IO密集型,上grid后发现多个线程起的driver实例都在一个node上面,而隔离性不是很好,然后会有些奇怪的报错。最后改用进程池方式,一个node一个driver,有点耗资源,却是针对此种情景的最优解。


2、错误截图都自个覆盖了怎么办?

对rf来说,每个新启动的pybot的进程都独立无交集,截图都从selenium-screenshot-1.png开始命名,同时运行时,就会被覆盖掉,日志里就找不到了。

本着能不改rf源码就不改的原则,最后还是改了两行,加上import 语句三行
Selenium2Library版本号 1.8.0,修改文件 Selenium2Library/keywords/_screenshot.py
顶行增加:
import random, string

capture_page_screenshot方法里,在原来的代码前增加两行:
    _rand_str = ''.join([random.choice(string.digits+string.letters) for i in range(6)])
filename = _rand_str + '-' + filename

 
六、还可以优化的地方

1、可以再改改,用ride也可以直接运行该脚本,前提是调试环境也足够壕

2、多data source日志合并会出错,所以暂不支持命令里写几个suite的,因为日志合并需要写很多很多的代码

3、有些不常用的pybot可选参数没有支持,可能支持也不会太好。有些并不需要支持

4、由于多个进程同时运行,message的打印,就有点临乱。。。

5、键盘中断运行,没有做。。。。


重要提醒:本文只针对用例级别的并行,也就是说,保证用例间独立是前提。suite级别的并行运行请出门拐至:https://github.com/mkorpela/pabot 。这家伙也是好久好久了才更新一次,所采用方法也基本无差别,他的用的线程池方式,不过好像启多个driver运行效果很不理想。
 

robotframework扩展发结果邮件功能

雪霁 发表了文章 • 9 个评论 • 2439 次浏览 • 2017-08-16 12:46 • 来自相关话题

PS:这篇文章纯粹是没事改着玩,刚好别人问到,如果有些事情可做可不做,那还是做吧
 
一、先说思路
用关键字的方式放在teardown里边肯定是不合适的,因为关键字肯定会在测试结果生成之前执行,所以要改rf的代码。改代码有一个思路就是增加一个pybot的可选参数,需要时执行,不然的话,每次调试一个用例运行一次都发一次邮件会很难受。思路有了,那就开始改代码吧!
 
二、再说实现
1  写一个发邮件的模块。保存成sendEmail.py,复制到$pythonpath/Lib/site-packages/robot目录下。
    注意,修改mail_user为自己的发件QQ邮箱,mail_pass设置为授权码,不会的请百度。有更复杂的需求自行扩展该文件。以下所有代码请自行找个靠谱的python IDE编辑#!/usr/bin/python
# -*- coding: UTF-8 -*-

import smtplib
from email.mime.text import MIMEText
import email.MIMEMultipart
from email.header import Header
import os
import mimetypes

def send_email(receivers= ['********@qq.com'], file_names=[], test_result=0):
# 第三方 SMTP 服务
print receivers
mail_host="smtp.qq.com" #设置服务器
mail_user="*******@qq.com" #用户名
mail_pass="************" #口令,QQ邮箱是输入授权码,在qq邮箱设置 里用验证过的手机发送短信获得,不含空格
sender = '********@qq.com'

#设置邮件中的测试结果
resultstr = '失败' if test_result else '通过'

main_msg = email.MIMEMultipart.MIMEMultipart()
message = MIMEText('''附件是本次自动化构建的报告,请注意查收 \n\n''', 'plain', 'utf-8')
main_msg.attach(message)
result = MIMEText('测试结果: '+resultstr, 'plain', 'utf-8')
main_msg.attach(result)
## 读入文件内容并格式化
for file_name in file_names:
data = open(file_name, 'rb')
ctype,encoding = mimetypes.guess_type(file_name)
if ctype is None or encoding is not None:
ctype = 'application/octet-stream'
maintype,subtype = ctype.split('/',1)
file_msg = email.MIMEBase.MIMEBase(maintype, subtype)
file_msg.set_payload(data.read())
data.close()
email.Encoders.encode_base64(file_msg)#把附件编码

basename = os.path.basename(file_name)
file_msg.add_header('Content-Disposition','attachment', filename = basename)#修改邮件头
main_msg.attach(file_msg)

main_msg['From'] = Header("robot自动发送", 'utf-8')
reciverstr = ';'.join(receivers)
main_msg['To'] = Header(reciverstr, 'utf-8')

subject = 'robotframework测试结果'
main_msg['Subject'] = Header(subject, 'utf-8')


try:
smtpObj = smtplib.SMTP_SSL()
smtpObj.connect(mail_host, 465)
smtpObj.login(mail_user,mail_pass)
smtpObj.sendmail(sender, receivers, main_msg.as_string())
print "邮件发送成功。"
except smtplib.SMTPException, e:
print "Error: 无法发送邮件。错误原因:", e

if __name__ == '__main__':
send_email(file_names=['E:\\pics\\1.png','E:\\pics\\2.jpg'])2 在/Lib/site-packages/robot/run.py修改两个地方
2.1 修改run.py处理send_email动作:
        先导入邮件模块: from robot.sendEmail import send_email
        再找到类RobotFramework的main函数,增加*号中间的如下行: def main(self, datasources, **options):
settings = RobotSettings(options)
LOGGER.register_console_logger(**settings.console_output_config)
LOGGER.info('Settings:\n%s' % unic(settings))
suite = TestSuiteBuilder(settings['SuiteNames'],
settings['WarnOnSkipped'],
settings['Extension']).build(*datasources)
suite.configure(**settings.suite_config)
if settings.pre_run_modifiers:
suite.visit(ModelModifier(settings.pre_run_modifiers,
settings.run_empty_suite, LOGGER))
with pyloggingconf.robot_handler_enabled(settings.log_level):
result = suite.run(settings)
LOGGER.info("Tests execution ended. Statistics:\n%s"
% result.suite.stat_message)
if settings.log or settings.report or settings.xunit:
writer = ResultWriter(settings.output if settings.log
else result)
writer.write_results(settings.get_rebot_settings())
#*********************************#
if len(settings['Email']):
send_email(settings['Email'],[settings.report,settings.log],
test_result=result.return_code)
#*********************************#
return result.return_code











2.2 还是run.py,修改USAGES里的options:
        增加如下行:Options
=======
-m --email email * set email options #增加这行,不要把注释复制
-F --extension value Parse only files with this extension when executing主要是给pybot命令行使用
 
3  找到robot/conf/settings.py文件修改settings
        找到 _cli_opts 设置,增加一条:'Email' : ('email', []),
4 修改robotide,找到$pythonpath/Lib/site-packages/robotide/contrib/testrunner/usages.py
      Options
=======
-m --email email * set email options#增加这行,不要把注释复制进去
-N --name name Set the name of the top level test suite. Underscores主要给ride设置时使用
 
三、运行及结果
    1  pybot运行方式,如需传多个人时,需后面再跟--email XXXX
        pybot --email XXXXXX@qq.com --email 978182471@qq.com "C:\\Users\\Administrator\\Desktop\\test.robot"
    2  ride 运行方式





    3  运行结果





 
四、我是怎么调试的

告诉大家一个调试rf代码的小窍门~~
工具:pycharm
方法:
    1、写一个简单的suite,如写单步调试evaluate,那就写个有evaluate关键字的用例,并将其打上debug的tag,如保存为:
             D:/RFtest/onedemo.txt
    2、找到 $python目录\Lib\site-packages\robot\run.py,用pycharm打开
    3、修改run.py的最后一句,将其注释,然后再加一句
            #run_cli(sys.argv[1:])
            run('D:/RFtest/onedemo.txt', include=['debug'])
    4、点击pycharm-->run-->debug,debug此run.py文件,断点可根据需要去打
    5、重要!!!Debug完了记得把run.py改回去,不然从ride或命令行运行就不能好好玩耍了
  查看全部
PS:这篇文章纯粹是没事改着玩,刚好别人问到,如果有些事情可做可不做,那还是做吧
 
一、先说思路
用关键字的方式放在teardown里边肯定是不合适的,因为关键字肯定会在测试结果生成之前执行,所以要改rf的代码。改代码有一个思路就是增加一个pybot的可选参数,需要时执行,不然的话,每次调试一个用例运行一次都发一次邮件会很难受。思路有了,那就开始改代码吧!
 
二、再说实现
1  写一个发邮件的模块。保存成sendEmail.py,复制到$pythonpath/Lib/site-packages/robot目录下。
    注意,修改mail_user为自己的发件QQ邮箱,mail_pass设置为授权码,不会的请百度。有更复杂的需求自行扩展该文件。以下所有代码请自行找个靠谱的python IDE编辑
#!/usr/bin/python
# -*- coding: UTF-8 -*-

import smtplib
from email.mime.text import MIMEText
import email.MIMEMultipart
from email.header import Header
import os
import mimetypes

def send_email(receivers= ['********@qq.com'], file_names=[], test_result=0):
# 第三方 SMTP 服务
print receivers
mail_host="smtp.qq.com" #设置服务器
mail_user="*******@qq.com" #用户名
mail_pass="************" #口令,QQ邮箱是输入授权码,在qq邮箱设置 里用验证过的手机发送短信获得,不含空格
sender = '********@qq.com'

#设置邮件中的测试结果
resultstr = '失败' if test_result else '通过'

main_msg = email.MIMEMultipart.MIMEMultipart()
message = MIMEText('''附件是本次自动化构建的报告,请注意查收 \n\n''', 'plain', 'utf-8')
main_msg.attach(message)
result = MIMEText('测试结果: '+resultstr, 'plain', 'utf-8')
main_msg.attach(result)
## 读入文件内容并格式化
for file_name in file_names:
data = open(file_name, 'rb')
ctype,encoding = mimetypes.guess_type(file_name)
if ctype is None or encoding is not None:
ctype = 'application/octet-stream'
maintype,subtype = ctype.split('/',1)
file_msg = email.MIMEBase.MIMEBase(maintype, subtype)
file_msg.set_payload(data.read())
data.close()
email.Encoders.encode_base64(file_msg)#把附件编码

basename = os.path.basename(file_name)
file_msg.add_header('Content-Disposition','attachment', filename = basename)#修改邮件头
main_msg.attach(file_msg)

main_msg['From'] = Header("robot自动发送", 'utf-8')
reciverstr = ';'.join(receivers)
main_msg['To'] = Header(reciverstr, 'utf-8')

subject = 'robotframework测试结果'
main_msg['Subject'] = Header(subject, 'utf-8')


try:
smtpObj = smtplib.SMTP_SSL()
smtpObj.connect(mail_host, 465)
smtpObj.login(mail_user,mail_pass)
smtpObj.sendmail(sender, receivers, main_msg.as_string())
print "邮件发送成功。"
except smtplib.SMTPException, e:
print "Error: 无法发送邮件。错误原因:", e

if __name__ == '__main__':
send_email(file_names=['E:\\pics\\1.png','E:\\pics\\2.jpg'])
2 在/Lib/site-packages/robot/run.py修改两个地方
2.1 修改run.py处理send_email动作:
        先导入邮件模块: 
from robot.sendEmail import send_email

        再找到类RobotFramework的main函数,增加*号中间的如下行:
    def main(self, datasources, **options):
settings = RobotSettings(options)
LOGGER.register_console_logger(**settings.console_output_config)
LOGGER.info('Settings:\n%s' % unic(settings))
suite = TestSuiteBuilder(settings['SuiteNames'],
settings['WarnOnSkipped'],
settings['Extension']).build(*datasources)
suite.configure(**settings.suite_config)
if settings.pre_run_modifiers:
suite.visit(ModelModifier(settings.pre_run_modifiers,
settings.run_empty_suite, LOGGER))
with pyloggingconf.robot_handler_enabled(settings.log_level):
result = suite.run(settings)
LOGGER.info("Tests execution ended. Statistics:\n%s"
% result.suite.stat_message)
if settings.log or settings.report or settings.xunit:
writer = ResultWriter(settings.output if settings.log
else result)
writer.write_results(settings.get_rebot_settings())
#*********************************#
if len(settings['Email']):
send_email(settings['Email'],[settings.report,settings.log],
test_result=result.return_code)
#*********************************#
return result.return_code











2.2 还是run.py,修改USAGES里的options:
        增加如下行:
Options
=======
-m --email email * set email options #增加这行,不要把注释复制
-F --extension value Parse only files with this extension when executing
主要是给pybot命令行使用
 
3  找到robot/conf/settings.py文件修改settings
        找到 _cli_opts 设置,增加一条:
'Email'            : ('email', []),

4 修改robotide,找到$pythonpath/Lib/site-packages/robotide/contrib/testrunner/usages.py
      
Options
=======
-m --email email * set email options#增加这行,不要把注释复制进去
-N --name name Set the name of the top level test suite. Underscores
主要给ride设置时使用
 
三、运行及结果
    1  pybot运行方式,如需传多个人时,需后面再跟--email XXXX
        pybot --email XXXXXX@qq.com --email 978182471@qq.com "C:\\Users\\Administrator\\Desktop\\test.robot"
    2  ride 运行方式

ride运行截图.png

    3  运行结果

邮件结果.png

 
四、我是怎么调试的

告诉大家一个调试rf代码的小窍门~~
工具:pycharm
方法:
    1、写一个简单的suite,如写单步调试evaluate,那就写个有evaluate关键字的用例,并将其打上debug的tag,如保存为:
             D:/RFtest/onedemo.txt
    2、找到 $python目录\Lib\site-packages\robot\run.py,用pycharm打开
    3、修改run.py的最后一句,将其注释,然后再加一句
            #run_cli(sys.argv[1:])
            run('D:/RFtest/onedemo.txt', include=['debug'])
    4、点击pycharm-->run-->debug,debug此run.py文件,断点可根据需要去打
    5、重要!!!Debug完了记得把run.py改回去,不然从ride或命令行运行就不能好好玩耍了