【Python】實現將Excel編寫的用例上傳到testlink指定用例集

背景

百科上說TestLink 是基於web的測試用例管理系統,主要功能是測試用例的創建、管理和執行,並且還提供了一些簡單的統計功能。其他的信息可以參照他們的官網http://www.testlink.org/

樓主所在的項目,需求、提測、測試等等都是使用的是gitlab的一個個issue加標籤管理的,用例的維護在開始的時候也是用的它。後來我們的直接上級職位發生了變更,新leader建議我們使用testlink。

試用了一段時間之後,發現一個非常令人詬病的地方–用例導入只支持xml格式,而且它本身的用例編寫也不太方便。

這也是我寫這個用例導入小工具的初衷。

思路

開始時博主想的是,把Excel編寫的用例(Excel應該是所有測試人員編寫用例的首選吧)轉換爲xml格式導入,後來發現更好的辦法-封裝Python testlink API。

寫得比較倉促,還有很多可以改進的地方,先用起來,後續優化。

具體實現

環境依賴

環境依賴 安裝方法
Python3
xlrd庫 pip install xlrd
testlink庫 pip install TestLink-API-Python-client

目錄結構

目錄結構如下,testCase目錄用於存放測試用例,upload_excel_data.py用於用例轉換上傳,logger_better.py用於記錄日誌,。

D:\PROJECT\UPLOAD_DATA2TESTLIN
│  logger_better.py
│  upload_excel_data.py
│
└─testCase
        testCase_Example.xlsx

使用方法

  • 登陸testlink後點擊上方個人賬號進入個人中心,新頁面點擊 ‘生成新的祕鑰’,使用該key替換掉upload_excel_data.py文件中的key值;

  • 使用get_projects_info函數獲取項目所在project_id,替換掉upload_excel_data.py中的project_id

  • 使用鼠標選中想要上傳用例的用例集,點擊右鍵獲取父節點ID,替換掉upload_excel_data.py中的father_id

upload內容

#! /usr/bin/python
# coding:utf-8 
""" 
@author:Bingo.he 
@file: upload_excel_data.py 
@time: 2018/05/03 
"""
import collections
import testlink
import xlrd
import os
from logger_better import Log

logger = Log(os.path.basename(__file__))

count_success = 0
count_fail = 0

def get_projects_info():
    project_ids = []
    projects = tlc.getProjects()
    for project in projects:
        project_ids.append({project['name']: project['id']})
    return project_ids

def get_projects_id():
    project_ids = []
    projects = tlc.getProjects()
    for project in projects:
        project_ids.append(project['id'])
    return project_ids

def get_suites(suite_id):
    """
    獲取用例集
    :return:
    """
    try:
        suites = tlc.getTestSuiteByID(suite_id)
        return suites
    except testlink.testlinkerrors.TLResponseError as e:
        # traceback.print_exc()
        logger.warning(str(e).split('\n')[1])
        logger.warning(str(e).split('\n')[0])
        return

def readExcel(file_path):
    """
    讀取用例數據
    :return:
    """
    case_list = []
    try:
        book = xlrd.open_workbook(file_path)  # 打開excel
    except Exception as error:
        logger.error('路徑不在或者excel不正確 : ' + str(error))
        return error
    else:
        sheet = book.sheet_by_index(0)  # 取第一個sheet頁
        rows = sheet.nrows  # 取這個sheet頁的所有行數
        for i in range(rows):
            if i != 0:
                case_list.append(sheet.row_values(i))  # 把每一條測試用例添加到case_list中
    return case_list

def check_excel_data(func):
    """
    參數有效性校驗
    :param func:
    :return:
    """

    def _check(*args, **kw):
        global count_fail
        global count_success

        # 校驗項目ID及測試集ID的有效性
        if not args[0] in get_projects_id():
            logger.error('project_id is not auth')
            return
        if not get_suites(args[1]):
            logger.error('father_id is not auth')
            return

        # 檢測測試數據的有效性
        for k, v in kw.items():
            if v == "" and k not in ['summary', 'importance']:
                logger.warning("TestCase '{title}' Parameter '{k}' is null".format(title=kw['title'], k=k))
        try:
            func(args[0], args[1], kw)
            count_success += 1
        except Exception as e:
            logger.error(e)
            count_fail += 1

    return _check

def format_info(source_data):
    """
    轉換Excel中文關鍵字
    :param source_data:
    :return:
    """
    switcher = {
        "低": 1,
        "中": 2,
        "高": 3,
        "自動化": 2,
        "手工": 1
    }
    return switcher.get(source_data, "Param not defind")

@check_excel_data
def create_testcase(test_project_id, suits_id, data):
    """
    :param test_project_id:
    :param suits_id:
    :param data:
    :return:
    """
    # 設置優先級默認值及摘要默認值
    if data['importance'] not in [1, 2, 3]:
        data['importance'] = 3
    if data["summary"] == "":
        data["summary"] = "無"

    # 初始化測試步驟及預期結果
    for i in range(0, len(data["step"])):
        tlc.appendStep(data["step"][i][0], data["step"][i][1], data["automation"])

    tlc.createTestCase(data["title"], suits_id, test_project_id, data["authorlogin"], data["summary"],
                       preconditions=data["preconditions"], importance=data['importance'], executiontype=2)

def excute_creat_testcase(test_project_id, test_father_id, test_file_name):
    # 對project_id father_id 做有效性判斷
    if test_project_id not in get_projects_id():
        logger.error('project_id is not auth')
        return
    if not get_suites(test_father_id):
        logger.error('father_id is not auth')
        return

    # 獲取用例
    test_cases = readExcel(os.path.join('testCase', test_file_name))
    if not isinstance(test_cases, collections.Iterable):
        return

    # 格式化用例數據
    for test_case in test_cases:
        testCase_data = {
            "title": test_case[0],
            "preconditions": test_case[1],
            "step": list(zip(test_case[2].split('\n'), test_case[3].split('\n'))),  # 以換行符作爲測試步驟的分界
            "automation": format_info(test_case[4]),  # 1  手工, 2 自動
            "authorlogin": test_case[5],
            "importance": format_info(test_case[6]),
            "summary": test_case[7]
        }

        create_testcase(test_project_id, test_father_id, **testCase_data)
    logger.info("本次操作共提交 {} 條數據,成功導入 {} 條,失敗 {} 條".format(count_success + count_fail, count_success, count_fail))

if __name__ == "__main__":
    url = "http://localhost/testlink/lib/api/xmlrpc/v1/xmlrpc.php"  # 替換爲testlink對應URL
    key = "3aca080de61e3e24b5be209a23fa0652"  # 這個key是錯誤的key,登陸testlink後點擊上方個人賬號進入個人中心,新頁面點擊 '生成新的祕鑰'獲取
    file_name = "testCase_Example.xlsx"
    project_id = "2354879"  # 可以通過 print(get_projects_info())獲取
    father_id = "2054879"  # 鼠標選中想要上傳用例的用例集,點擊右鍵獲取父節點ID
    tlc = testlink.TestlinkAPIClient(url, key)

    # print("項目信息: ", get_projects_info())
    excute_creat_testcase(project_id, father_id, file_name)

logger內容

#! /usr/bin/python
# coding:utf-8 
""" 
@author:Bingo.he 
@file: logger_better.py 
@time: 2018/02/12 
"""  
import logging
import time
import os

cur_path = os.path.dirname(os.path.realpath(__file__))
log_path = os.path.join(cur_path, 'logs')

if not os.path.exists(log_path):  os.mkdir(log_path)

class Log():
    def __init__(self, logger, logname='{}.log'.format(time.strftime('%Y-%m-%d'))):
        self.logname = os.path.join(log_path, logname)
        self.logger = logging.getLogger(logger)
        self.logger.setLevel(logging.DEBUG)
        self.formatter = logging.Formatter('[%(asctime)s]-[%(name)s]-%(levelname)s: %(message)s')

    def __console(self, level, message):
        fh = logging.FileHandler(self.logname, 'a', encoding='utf-8')
        fh.setLevel(logging.DEBUG)
        fh.setFormatter(self.formatter)
        self.logger.addHandler(fh)

        ch = logging.StreamHandler()
        ch.setLevel(logging.DEBUG)
        ch.setFormatter(self.formatter)
        self.logger.addHandler(ch)

        if level == 'info':
            self.logger.info(message)
        elif level == 'debug':
            self.logger.debug(message)
        elif level == 'warning':
            self.logger.warning(message)
        elif level == 'error':
            self.logger.error(message)
        self.logger.removeHandler(ch)
        self.logger.removeHandler(fh)
        fh.close()

    def debug(self, message):
        self.__console('debug', message)

    def info(self, message):
        self.__console('info', message)

    def warning(self, message):
        self.__console('warning', message)

    def error(self, message):
        self.__console('error', message)

if __name__ == "__main__":
    log = Log(os.path.basename(__file__))
    log.info("---測試開始----")
    log.info("操作步驟1,2,3")
    log.warning("----測試結束----")

其他:

  • 用例步驟分隔符:當前使用換行符分隔,可修改excute_creat_testcase函數中testCase_data的step參數
  • Excel中作者信息必須與提供的key值對應

測試用例格式

在此感謝該API的作者。

Python testlink API Github項目地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章