安装环境
有个比较好的教程getting-started-robotfamework
如果需要在Linux上运行:
- 准备一个Linux机器,安装robot framework
robotframework_pip_list.txt - 开通samba
Quick setup samba service - 准备一个Windows机器,安装ride
如果需要在Linux上写UI automation
- 安装seleniumLibrary:
pip install --upgrade robotframework-seleniumlibrary
- 安装Chrome + Chromedriver
yum install google-chrome-stable
其他好用的安装方法
3. 找到chrome对应的chromedriver 版本,并下载 https://chromedriver.chromium.org/
4. 将下载的chromedriver 放到$PATH里的目录下
5. 在/usr/bin/google-chrome 脚本的最后填加参数
常用keyword
官方教程
Standard-libraries
SeleniumLibrary
RequestsLibrary
run keyword if
set variable if
Run
Fetch From Right
Fetch From Left
Sleep
Wait Until Keyword Succeeds
SSHLibrary.Open Connection
I
P
4
a
l
i
a
s
=
{IP4} alias=
IP4alias={Alias}
Run Keyword If ‘${usekey}’==‘True’ SSHLibrary.Login With Public Key
${ret} SSHLibrary.Execute Command ${cmd}
SSHLibrary.close Connection
SSHLibrary.Switch Connection ${ssh_session}
Should Contain
Should be Equal/Should be Equal As Strings/Should be Equal As Integers
Open Browser
maximize browser window
wait until element is visible
Select Frame
Unselect Frame
wait until page contains element
Wait Until element Contains
frame Should Contain
Get Element Attribute
close all browsers
可以按住Control键查看keyword用法
开始构建
简单写了两个case,经常用到的Libraries如图。
其中config.robot 可以放一些用户名密码或者key等不应该放到git上的信息,然后把这个文件放到git ignore里,不上传。
运行方法
Example:
robot --test “casename” robotfile
robot -i sometag robotpath
robot -e deletedORnotReady -i BVT -d logPath --output output1.xml testsuitesPath
robot --rerunfailed logPath/output1.xml --output logPath/output2.xml testsuitesPath
解析log
GenerateContent.py
import os
import sys
import PasRes
import re
import AnalyzeRerunResults
import time
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
CONTENT_FORMATER = '''
<html>
<head>
<style>
body {
font-size: 20px;
font-family: Calibri;
line-height: 19px;
}
</style>
</head>
<body>
Dear All:<br/>
This is the <strong>automation test report.</strong><br/>
<strong>Start Time:</strong> %s<br/>
<strong>Elapsed Time:</strong> %s<br/>
<strong>Total cases:</strong> %s<br/>
<strong>Failed cases:</strong> <font color="red">%s</font><br/>
<strong>Failed cases list:</strong><br/>
<font color='red'>%s</font>
<br/>
Thanks.<br/>
</body>
</html>
'''
def ParseRes(respath):
f = o
pen(respath,'r')
resstr = f.read()
f.close()
reg = '{.*"elapsed":".*?","fail":(.*?),"label":"All Tests","pass":(.*?)}'
m = re.search(reg,resstr)
return m.group(1), m.group(2)
def ParseStime(respath):
f = open(respath, 'r')
str = f.read()
f.close()
begin = 'window.output["baseMillis"] = '
end = ";"
index = str.find(begin)
index+=len(begin)
sub_str = str[index:]
index = sub_str.find(end)
#print sub_str[:index]
millsec = int(sub_str[:index])
StartTime = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(millsec/1000))
reg = '{.*"elapsed":"(.*?)","fail":.*?,"id":".*?","label":"Testsuites","name":"Testsuites","pass":.*?}'
elapTime = re.search(reg,str)
return StartTime,elapTime.group(1)
def SummaryRes(res):
if res == None:
print 'get result dictionary fail!'
sys.exit(1)
else:
sum = int(res[0]) + int(res[1])
fl = res[0]
if int(res[0]) > 0:
flag = 'FAIL'
else:
flag = 'PASS'
return str(sum), fl, flag
def gen_fclist_htmlstring(cl,fcl):
clstr = ''
fclstr = ''
for l in cl:
clstr = clstr + str(l) + '<br/>'
for i in fcl:
fclstr = fclstr + str(i) + '<br/>'
return clstr,fclstr
def get_fail_case_list(outputpath):
tree = ET.parse(outputpath)
count = 0
failcasesname = []
for elem in tree.iter(tag='test'):
elemattri = elem.attrib
for key in elemattri:
if key == "name":
testname = elemattri[key]
status = elem.find("status")
for key in status.attrib.keys():
if status.attrib[key] == "FAIL":
failcasesname.append(testname)
str_fail_list = ''
for l in failcasesname:
str_fail_list = str_fail_list + str(l) + '<br/>'
print str_fail_list
return str_fail_list
def gen_title_resultcontent(ReportFile, OutputFile):
pt = ParseStime(ReportFile)
sres = ParseRes(ReportFile)
res = SummaryRes(sres)
print res
subject = 'AUTOMATION TEST REPORT :' + res[2]
fl = get_fail_case_list(OutputFile)
content = CONTENT_FORMATER % (str(pt[0]), str(pt[1]), str(res[0]), str(res[1]), fl)
return subject,content
if __name__ == '__main__':
sub,cont = gen_title_resultcontent()
print sub
print cont
发送report
# -*- coding: utf-8 -*-
import types
import os
import logging
import boto
import GenerateContent
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import COMMASPACE
from email import Encoders
from boto.ses import SESConnection
import sys
import ftplib
import datetime
rootPath = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
logPath = os.path.join(rootPath, 'log')
binPath = os.path.join(rootPath, 'bin')
LogFilePath = os.path.join(logPath,'log.html')
ReportFilePath = os.path.join(logPath,'report.html')
OutputFilePath = os.path.join(logPath,'output.xml')
class AmazonSender(object):
client = None
def __init__(self, aws_key, aws_secret,proxy=None,proxy_port=None):
self.aws_key = aws_key
self.aws_secret = aws_secret
self.proxy = proxy
self.proxy_port = proxy_port
def send_email(self, sender,
to_addresses,
subject,
text,
html=None,
files = [],
fileDisplayName = '',
reply_addresses=None,
sender_ascii=None):
if not sender_ascii:
sender_ascii = sender
client = self.get_client()
message = MIMEMultipart('alternative')
message.set_charset('UTF-8')
message['Subject'] = _encode_str(subject)
message['From'] = _encode_str(sender)
message['To'] = _convert_to_strings(to_addresses)
if reply_addresses:
message['Reply-To'] = _convert_to_strings(reply_addresses)
message.attach(MIMEText(_encode_str(text), 'plain'))
for file in files:
part = MIMEBase('application', "octet-stream")
part.set_payload( open(file,"rb").read() )
Encoders.encode_base64(part)
if fileDisplayName:
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(fileDisplayName))
else:
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(file))
message.attach(part)
if html:
message.attach(MIMEText(_encode_str(html), 'html'))
SENDMAIL_MAX_RETRY = 1
for i in xrange(SENDMAIL_MAX_RETRY):
try:
client.send_raw_email(message.as_string(), sender_ascii, destinations=to_addresses)
return True
except Exception,e:
logging.exception(e.message)
return False
def vertify_email(self, email):
client = self.get_client()
return client.verify_email_address(email)
def get_client(self):
if not self.client:
self.client = boto.ses.connect_to_region(
'us-west-2',
aws_access_key_id=self.aws_key,
aws_secret_access_key=self.aws_secret)
return self.client
#--- Helpers ----------------------------------------------
def _convert_to_strings(list_of_strs):
if isinstance(list_of_strs, (list, tuple)):
result = COMMASPACE.join(list_of_strs)
else:
result = list_of_strs
return _encode_str(result)
def _encode_str(s):
if type(s) == types.UnicodeType:
return s.encode('utf8')
return s
def send_report_email():
title,contenttext = GenerateContent.gen_title_resultcontent(ReportFilePath,OutputFilePath)
recvList = ['testauto@testauto.com']
sender = AmazonSender(sys.argv[1],sys.argv[2])
sender.send_email('testauto@testauto.com', recvList, title,text=None,html=contenttext, files=[])
def uploadThis(myFTP, path):
files = os.listdir(path)
for f in files:
fh = open(f, 'rb')
myFTP.storbinary('STOR %s' % f, fh)
fh.close()
def UploadReportDirToFTP(LocalDir,FTPServer,Usr,Pwd,FTPPath):
myFTP = ftplib.FTP(FTPServer, Usr, Pwd)
myFTP.cwd(FTPPath)
d = datetime.datetime.now()
dsuf = d.strftime("%b-%d-%y-%H-%M")
myFTP.mkd(dsuf)
myFTP.cwd(dsuf)
os.chdir(LocalDir)
uploadThis(myFTP,LocalDir)
if __name__ == "__main__":
sendmailpath = os.path.abspath(__file__)
logpath = os.path.join(os.path.dirname(sendmailpath), "..","..","log")
# UploadReportDirToFTP(logpath, 'server', 'user', 'password', ReportLocation)
send_report_email()
脚本放到git上,然后通过Jenkins job定时触发
pipeline{
agent{
node{label 'automation'}
}
parameters {
choice choices: ['a', 'b', 'c', 'd'], description: '', name: 'ENV'
choice choices: ['x', 'y'], description: '', name: 'Region'
listGitBranches branchFilter: 'refs.*/(.*)', credentialsId: '', defaultValue: 'develop', name: 'Branch', quickFilterEnabled: false, remoteURL: 'git@test.git', selectedValue: 'DEFAULT', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH'
}
stages{
stage('RunCase'){
steps{
echo "Running Case!"
script{
def branches = [:]
branches["RunCaseA"] = {
build job: 'Automation_parallel_A',parameters: [
[$class: 'StringParameterValue', name: 'ENV', value:String.valueOf(ENV)],
[$class: 'StringParameterValue', name: 'Region', value:String.valueOf(Region)]]
}
branches["RunCaseB"] = {
build job: 'Automation_parallel_B',parameters: [
[$class: 'StringParameterValue', name: 'ENV', value:String.valueOf(ENV)],
[$class: 'StringParameterValue', name: 'Region', value:String.valueOf(Region)]]
}
parallel branches
}
}
}
stage('SendMail'){
steps{
build job: 'Sendmail',parameters: [
[$class: 'StringParameterValue', name: 'ENV', value:String.valueOf(ENV)],
[$class: 'StringParameterValue', name: 'Region', value:String.valueOf(Region)]]
}
}
}
}
Build periodically: H 23 * * *
这样基本流程就通了,补产品相关的case就可以了。有不足的以后补充吧。