背景是這樣的:
這個組的測試人員每跑一個case都要上傳測試結果附件到QC。每一個待測功能模塊可能包含幾十上百的case。因而手工上傳測試結果變成了繁重的體力勞動。使人驚訝的是咱們的工具開發組居然說作不了QC的測試結果附件上傳。更讓我驚訝的是,測試人員居然真的手工上傳結果上傳了大半年了。
如下我寫了個小工具解決這個問題:
思路很簡單,調用hp提供的ALM Rest api接口。把一個個用戶操做轉化成http請求。而後按照接口要求,把附件一個一個上傳到指定的QC test instance上。
主要用的庫是:
requests:負責發送http請求
BeautifulSoup4:負責解析QC服務器返回的響應。
整個工具很是簡單。
html
具體步驟以下:
先讓用戶登陸進QCexpress
[2017-05-17 13:57:25,023] Starting new HTTP connection (1): XXXXX.com [2017-05-17 13:57:25,430] post http://XXXXX.com:80/qcbin/authentication-point/authenticate [2017-05-17 13:57:25,430] headers = {'Authorization': 'Basic XXXXXXX'} [2017-05-17 13:57:25,430] 200 OK [2017-05-17 13:57:25,430] --------------------
(上面這個帶了時間戳的東西是個人log。每一個請求都會本身記下來發了些啥。)
而後按照用戶給定的的test_set_id去QC裏搜索一下這個test set下的test instance有哪些。api
[2017-05-17 13:57:25,430] -------------------- [2017-05-17 13:57:26,303] get http://XXXXX.com:80/qcbin/rest/domains/{Domain name}/projects/{Project name}/test-instances?query={contains-test-set.id[XXXXXX]}&fields=id,test-id&page-size=2000 [2017-05-17 13:57:26,303] 200 OK [2017-05-17 13:57:26,303] --------------------
而後每一個test instance id去查一下qc裏對應的test id,也就是測試用例的id,再根據這個id,從qc裏查出測試用例的名字。
搜索的時候,這樣指定返回值的字段名,如下是搜索某個test set id下的test-instance,而且要求返回值只包含test instance id和 test id
test-instances?query={contains-test-set.id[XXX]}&fields=id,test-id&page-size=2000服務器
[2017-05-17 13:57:26,303] -------------------- [2017-05-17 13:57:26,516] get http://XXXXX.com:80/qcbin/rest/domains/XXX/projects/XXX/tests?query={id[XXXXXX]}&fields=name&page-size=2000 [2017-05-17 13:57:26,516] 200 OK [2017-05-17 13:57:26,516] --------------------
根據上面查出來的test-id再次查詢,獲得這個test的name字段值
查出了測試用例的名字是用來和我要上傳的附件作匹配的,這兩個名字對上了,就會把某個附件傳到這個用例對應的test-instance上。
上傳文件部分調用的是這樣的一個requests請求:session
data = { 'filename': ("", attachment_name), 'override-existing-attachment':("","y") } file = { 'file': open(attachmentpath+attachment_name, 'rb') } response = self.session.post(url, files=file,data=data)
最後附上一些代碼,供參考:
這個類是用來作get請求和post請求的。auto_log裝飾器會把發了什麼請求打成log。
不像某些QC操做的庫裏同樣滿屏的打logapp
import logging as log import requests class RestClient: def __init__(self): self.headers={} self.session = requests.Session() log.basicConfig(level=log.INFO, format='[%(asctime)s] %(message)s') def auto_log(func): def wrapper(*args, **kw): r = func(*args, **kw) log.info("%s %s", func.func_name, args[1]) for key in kw: log.info("%s = %s",key, kw[key]) log.info("%s %s",r.status_code,r.reason) log.info("--------------------") return r return wrapper @auto_log def get(self, url): return self.session.get(url) @auto_log def post(self, url, files=None, data=None,headers=None): if headers is None: headers = self.headers return self.session.post(url, headers=headers,files=files,data=data)
第二個類是用來操做QC的,如今我作了用戶登陸、搜索entity、添加附件三個功能。後續還會加上更新entity和建立entity等,把參考文檔裏的common tasks全都實如今這個類裏。
dom
class QCSession: def __init__(self, username, password, qc_url): self.api_client=RestClient() auth = base64.b64encode("{}:{}".format(username, password)) self.api_client.session.headers['Authorization'] = "Basic {}".format(auth) self.qc_url = qc_url self.login() def login(self): r = self.api_client.post(self.qc_url.auth, headers=self.api_client.session.headers) assert(r.status_code is 200) def get(self, *args): r = self.api_client.get(self.qc_url.path(*args)) assert (r.status_code is 200 or r.status_code is 201) return r def add_attachment(self, filename, over_ride,*args): data = { 'filename': ("", filename), 'override-existing-attachment': ("", over_ride) } files = { 'file': open(filename, 'rb') } r = self.api_client.post(self.qc_url.path(*args),data=data,files=files) assert (r.status_code is 200 or r.status_code is 201) return r def query_entitys(self, entity_name, query_expression, fields=None, page_size="2000"): """ :param entity_name: the entity to query :param query_expression: the query expression :param fields: the files to return in results :param page_size: the result page size, by default is 2000 which is the max value :return: resturn the BeautifulSoup parsed result """ query_url=u"%s?query={%s}&fields=%s&page-size=%s" %(entity_name,query_expression,fields,page_size) r = self.get(query_url) res = BeautifulSoup(r.content, "lxml") return res
還有一個保存和生成QC的各類url地址的類:ide
class QCURL: def __init__(self, hostname, domain, project, port='80'): self.__base = u'http://{}:{}/qcbin/'.format(hostname,port) self.__work = self.__base + u'rest/domains/{}/projects/{}/'.format(domain,project) self.auth = self.__base + u'authentication-point/authenticate' self.logout = self.__base + u'authentication-point/logout/' def path(self, *args): return self.__work + '/'.join([str(arg) for arg in args])
還有第四個類就是用QC Session類來實現我要的業務功能了。具體代碼就不貼了,裏面沒什麼可讓別人重用的東西。
拿上面三個類就能夠用了。
給個例子:
登陸QC,並查詢指定的testset_id下全部的test-instances,並返回其id和test-id
下面全部沒賦值的變量都換成本身環境的真實值的字符串就好了。工具
qc_url = QCURL(hostname, domain, project, port) qc_session = QCSession(username, password, qc_url) qc_session.query_entitys('test-instances', 'contains-test-set.id[%s]' % testset_id, "id,test-id")
但願這篇文章能夠幫到一些有一樣問題的人。以上代碼在HP ALM 11.52上測試經過。
參考文檔:http://alm-help.saas.hpe.com/en/12.53/api_refs/REST_TECH_PREVIEW/ALM_REST_API_TP.htmlpost