openpyxl使用介绍

概况

最近,公司需要搭建接口测试框架,希望用excel来维护用例
yaml、json不好吗?要是这样,就可以直接用httprunner,但是吧,你懂的

那就先看看python怎么操作excel吧。python操作excel的库很多,可参看Working with Excel Files in Python,这里主要介绍也比较推荐使用 openpyxl

openpyxl

openpyxl安装及使用

安装

pip3 install openpyxl

基本使用

excel 组成关系:workbook -> sheet -> cell

workbook及sheet创建及获取

  • 常用函数或属性
    • wb.active
    • ws.title
    • wb.save
from openpyxl import Workbook

wb = Workbook()

# workbook至少有一个sheet,默认为Sheet
ws = wb.active  # 默认是第一个sheet
print(1, ws)

# 创建新的sheet
ws1 = wb.create_sheet()  # 创建sheet, title为Sheet1。sheet未指定title时,默认title依次为Sheet,Sheet1,Sheet2...
ws2 = wb.create_sheet("new_sheet")  # 创建sheet,sheet title为 new_sheet
ws3 = wb.create_sheet("new_sheet_2", 0)  # 创建sheet插入在第一个

# 获取sheet title
print(2, ws2.title)

# 修改sheet title
ws3.title = "rename_sheet"

# 获取表格所有的sheet title
print(3, wb.sheetnames)
for sheet in wb:
    print(sheet.title)

# 根据sheet title来选取sheet
ws = wb['new_sheet']
print(ws)

# 复制sheet
source = wb["new_sheet"]
target = wb.copy_worksheet(source)

wb.save('test.xlsx')

sheet数据操作

  • 常用函数或属性
    • ws.cell、ws.cell.value
    • ws.append
    • ws.rows、ws.values
    • ws.iter_rows(values_only=True)、ws.iter_cols(values_only=True)
from openpyxl import Workbook
from openpyxl.utils import get_column_letter

wb = Workbook()
ws = wb.active

# 写入cell值
# 方式1:x行A列
ws['A1'] = "100"
ws['A2'] = 200

# 方式2:使用 Worksheet.cell()方法,指定row行(int),column列(int)
ws.cell(row=3, column=1, value=300)
ws.cell(row=4, column=1).value = 400  # 获取cell后赋值
c = ws['A5']
c.value = 500

print(ws.cell(2, 1).value)

# 方式3:写入多个cell的值
ws.append([1, 2, 3])

'''
get_column_letter(idx, ) 将数字转换成列值 如 3 -> 'C'
'''
# 使用循环写入
ws2 = wb.create_sheet(title="Data")
for row in range(10, 20):
    for col in range(27, 54):
        _ = ws2.cell(column=col, row=row, value="{0}".format(get_column_letter(col)))

# 读取cell值
# 方式1:ws.values 或 ws.rows
print(ws.values)
for row in ws.values:
    print(row)  # 行, 元组
    for value in row:
        print(value)

for row in ws.rows:
    for cell in row:
        print(cell.value)

# 方式2:通过iter_rows,iter_cols
for row in ws.iter_rows(values_only=True):  # values_only=True返回仅为value
    print(row, '~~~')  # 行,元组
    for value in row:
        print(value, "+++")

for cols in ws.iter_cols(values_only=True):
    print(cols, "~~~")  # 列,元组
    for value in cols:
        print(value, "---")

wb.save('test.xlsx')

sheet数据其他操作

from openpyxl import Workbook
import datetime

wb = Workbook()
ws = wb.active

ws["A1"] = datetime.datetime(2021, 12, 12)
print(ws["A1"].value)
print(ws["A1"].number_format)


# 切片访问单元格区域
cell_range = ws['A1':'C2']
for rows in cell_range:
    print(rows)
    for i in rows:
        print(i.value)

colC = ws['C']
col_range = ws['C:D']
row10 = ws[10]
row_range = ws[5:10]

wb.save("text.xlsx")

打开现有excel文件

  • load_workbook()
  • ws.max_row
  • ws.max_column
from openpyxl import load_workbook, Workbook

wb = load_workbook('test.xlsx')
ws = wb.active
print(ws.max_row)
print(ws.max_column)

Read-only 和 Write-only 模式

  • wb.close()
from openpyxl import load_workbook
wb = load_workbook(filename='large_file.xlsx', read_only=True)
ws = wb['big_data']

for row in ws.rows:
    for cell in row:
        print(cell.value)

# 读取文件后使用close关闭文档
wb.close()

不同于普通的workbook, read-only workbook会使用懒加载的方式加载,因此这种方式加载的workbook必须明确使用close()方法来关闭

from openpyxl import Workbook

wb = Workbook(write_only=True)
ws = wb.create_sheet()

for irow in range(100):
    ws.append(['%d' % i for i in range(200)])

wb.save('new_big_file.xlsx')

不同于普通的workbook,新创建的write-only workbook默认不包含任何worksheet,worksheet 必须通过create_sheet() 方法来创建
在write-only workbook中,行的添加只能通过append()添加
在write-only workbook 只能保存一次,之后的对该workbook的操作则会报错

demo实战

约定用例格式api.xlsx如:

用例编号 请求地址 请求方式 预期结果 测试结果
1 https://www.baidu.com GET baidu
2 https://www.baidu.com GET baidu
3 https://www.baidu.com GET baidu
4 https://www.baidu.com GET baidu
5 https://www.baidu.com GET baidu
import requests
from openpyxl import Workbook, load_workbook


def read_testcase():
    wb = load_workbook("testcases/api.xlsx")
    ws = wb.get_sheet_by_name('test_case')
    case_num = ws.max_row

    interface_list = []

    # 循环获取表格当中的内容
    for row in range(2, case_num + 1):
        case = []
        for col in range(2, 5):
            if col == 2:
                url = ws.cell(row, col).value
                case.append(url)
            elif col == 3:
                method = ws.cell(row, col).value
                case.append(method)
            else:
                expected_res = ws.cell(row, col).value
                case.append(expected_res)
        interface_list.append(case)
    return interface_list


def write_test_result(test_result):
    wb = load_workbook('testcases/api.xlsx')
    ws = wb.get_sheet_by_name('test_case')
    case_num = ws.max_row
    for row in range(2, case_num + 1):
        ws.cell(row=row, column=5, value=test_result[row - 2])
    wb.save('testcases/api.xlsx')
    wb.close()


def test_baidu():
    interface_list = read_testcase()
    print(interface_list)
    test_result = []
    
    # 循环执行接口测试
    for i in interface_list:
        r = requests.request(method=i[1], url=i[0])
        try:
            assert i[2] in r.text
            test_result.append('pass')
        except:
            test_result.append('fail')
    print(test_result)
    # 回写测试结果
    write_test_result(test_result)

仅一个小的demo,还有很多问题需要解决,如接口之间的交互等,可进一步参考httprunner的设计哲学
思路:可尝试建立excel参数与httprunner用例参数之间的映射关系

  • 更多特性请参看官方文档,官方文档是最好的学习资料