UI测试
自动录制
python -m playwright codegen --viewport-size "1920,1080" -o .\test_w.py
#指定窗口大小
#输出录制内容到指定文件
pytest + playwright 三种执行方式
1.命令行运行
pytest + run.py #模块名称 检测当前目录下的测试case执行 可以在pytest后添加参数 import os import pytest if name=='main': pytest.main(['-vs', './testcase/login/test_login_usererror.py', '--alluredir', './temp']) #pytest.main(['-vs', './testcase/', '--alluredir', './temp']) os.system('allure generate ./temp -o ./report --clean') #生成正式的测试报告 timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S') #截取当前时间戳 report_path = f'./report/report_{timestamp}' #生成新的时间戳文件夹路径 os.system(f'allure generate ./temp -o {report_path} --clean') #生成每一次当前时间执行的的测试报告 注意下main内的参数文件名称 指定文件就执行指定文件 指定文件夹就执行指定文件夹的测试用例
2.IDE直接运行
3.pytest.ini运行
配置文件为pytest.ini
[pytest]
addopts=-vs --alluredir=./temp --clean-alluredir #命令行参数使用空格进行分割
testpaths=./testcase #测试用例文件夹
python_files=test_.py #模块文件
python_classes=Test #测试类名
python_functions=test #测试函数名
使用yaml文件存储测试数据
loginpage:
- title: 登录
des: 登录的测试用例
cases:
- name: 登录页
method: goto
url: http://172.11.32.82:9002/Account/Login?ReturnUrl=%2F
- name: 输入用户名
method: fill
selector: //*[@id="UserName"]
values: "*****" 此处输入用户名
- name: 输入密码
method: fill
selector: //*[@id="password_inputs"]
values: "*****" 此处输入密码\
- name: 点击登录
method: click
selector: //*[@id="app"]/main/div/div[2]/div/button/span
目前使用结构
common #存放通用模块
testcase #存放测试用例
testdata #存放测试数据
temp #存放临时文件
report #存放最新测试报告
使用allure的测试报告插件
模块间引用
在A.py种编写Test_a类如果需要在B.py中引用
在B.py文件中编写如下
from A import Test_a
公共方法
import pytest
from playwright.sync_api import sync_playwright
@allure.feature('Playwright EMS2.0 Forend')
class TestRun:
def setup_class(self):
self.playwright = sync_playwright().start()
self.browser = self.playwright.chromium.launch(headless=False, args=['--start-maximized'])
self.context = self.browser.new_context()
self.page = self.context.new_page()
def teardown_class(self):
self.page.wait_for_timeout(3000)
self.browser.close()
self.playwright.stop()
def run_step(self, func, *args):
func(*args)
def run_case(self, cases):
allure.dynamic.title(cases[0]['title'])
allure.dynamic.description(cases[0]['des'])
cases = cases[0]['cases']
try:
for case in cases:
self.page.wait_for_timeout(3000)
method = case['method']
if method == 'goto':
url = case['url']
with allure.step(case['name']):
self.run_step(self.page.goto, url)
elif method == 'click':
selector = case['selector']
with allure.step(case['name']):
self.run_step(self.page.click, selector)
elif method == 'fill':
selector = case['selector']
values = case['values']
with allure.step(case['name']):
self.run_step(self.page.fill, selector,values)
except Exception:
self.page.wait_for_timeout(2000)
allure.attach(self.page.screenshot(), '用例失败截图', allure.attachment_type.PNG)
pytest.fail('测试用例失败')
self.page.wait_for_timeout(2000)
allure.attach(self.page.screenshot(), '用例执行成功截图', allure.attachment_type.PNG)
例如:
登录调用统一执行每一步用例
#读取测试用例数据
def load_test_ui_file(case_type):
test_files = [
'F:/data_test/test_w.yaml'
]
test_data = {}
for file in test_files:
with open(file, encoding='utf-8') as f:
data = yaml.safe_load(f)
test_data.update(data)
return test_data[case_type]
class TestUi(TestRun):
@pytest.mark.parametrize('cases', [
load_test_ui_file('loginpage'),
#load_test_ui_file('passworderrorpage'),
# load_test_ui_file('getscreen2')
])
def test_ui(self, cases):
self.run_case(cases)
断言使用
#获取页面文字做断言
content = self.page.text_content('//html/body/div[1]/div[2]/div[2]/div[2]/div/div[1]/div[5]/div/div[1]/div[2]')
#print(content)
assert content == "调度信息"
接口测试
pytest + requests + allure 三种执行方式
1.命令行运行 pytest + run.py #模块名称 检测当前目录下的测试case执行 可以在pytest后添加参数 import os import pytest if name=='main': pytest.main(['-vs', './testcase/login/test_login_usererror.py', '--alluredir', './temp']) #pytest.main(['-vs', './testcase/', '--alluredir', './temp']) os.system('allure generate ./temp -o ./report --clean') #生成正式的测试报告 timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S') #截取当前时间戳 report_path = f'./report/report_{timestamp}' #生成新的时间戳文件夹路径 os.system(f'allure generate ./temp -o {report_path} --clean') #生成每一次当前时间执行的的测试报告 注意下main内的参数文件名称 指定文件就执行指定文件 指定文件夹就执行指定文件夹的测试用例
2.IDE直接运行
3.pytest.ini运行 配置文件为
pytest.ini
[pytest]
addopts=-vs --alluredir=./temp --clean-alluredir #命令行参数使用空格进行分割
testpaths=./testcase #测试用例文件夹
python_files=test_.py #模块文件
python_classes=Test #测试类名
python_functions=test #测试函数名
使用yaml文件存储测试数据 getadapterlist 三个测试用例
getadapterlist:
- title: 获取控制单元列表
des: 获取所有控制单元的基础信息
cases:
- name: 获取所有控制单元的基础信息
url: http://172.11.32.82:8802/api/InstitutionMng/GetAdapterList/
method: POST
headers:
Content-Type: application/json
payload:
{"pageIndex":1,"pageSize":10,"queryModel":{"Id":"","Name":"","StationId":[],"CompanyId":""}}
- name: 获取所有控制单元的基础信息url地址错误
url: http://172.11.32.82:8802/api/InstitutionMng/GetAdapterList1/
method: POST
headers:
Content-Type: application/json
payload:
{ "pageIndex": 1,"pageSize": 10,"queryModel": { "Id": "","Name": "","StationId": [ ],"CompanyId": "" } }
- name: 获取所有控制单元的基础信息分页错误
url: http://172.11.32.82:8802/api/InstitutionMng/GetAdapterList/
method: POST
headers:
Content-Type: application/json
payload:
{ "pageIndex": 0,"pageSize": 10,"queryModel": { "Id": "","Name": "","StationId": [ ],"CompanyId": "" } }
目前使用结构
common #存放通用模块
testcase #存放测试用例
testdata #存放测试数据
temp #存放临时文件
report #存放最新测试报告
使用allure的测试报告插件
测试报告标题
allure.dynamic.title(cases['title'])
测试报告描述
allure.dynamic.description(cases['des'])
allure.story('登录成功')
执行步骤
allure.step("开始执行")
class Test_api:
def api_case(self,cases):
# 测试报告标题
allure.dynamic.title(cases['title'])
# 测试报告描述
allure.dynamic.description(cases['des'])
#indexpage = cases['indexpage']
cases = cases['cases']
with allure.step("开始执行"):
try:
for case in cases:
# 添加Allure测试步骤
url = case['url']
method = case['method']
headers = case['headers']
payload = case['payload']
name = case['name']
with allure.step(f"请求说明: {name}"):
allure.attach(name, "请求说明")
with allure.step(f"请求URL: {url}"):
allure.attach(url, "请求URL")
self.response = requests.request(method, url, headers=headers, json=payload)
with allure.step(f"请求参数: {payload}"):
allure.attach(str(payload), "请求参数")
#context_dict2 = self.response.headers
#返回数据转成json格式 其实是一个字典
#context_dict = self.response.json()
with allure.step("验证响应状态码"):
allure.attach(str(self.response.status_code), "响应状态码")
with allure.step("响应时间"):
allure.attach(str(self.response.elapsed.total_seconds()), "输出响应时间")
#assert self.response.status_code == 200
if self.response.status_code == 200 and self.response.json()['code'] == 0:
with allure.step("执行成功信息"):
allure.attach(self.response.content, "执行成功返回信息", allure.attachment_type.JSON)
elif self.response.status_code != 200:
with allure.step("用例执行失败信息"):
allure.attach(str(self.response.status_code), '执行失败信息')
else:
with allure.step("用例执行失败信息"):
allure.attach(self.response.content, "执行失败信息", allure.attachment_type.JSON)
except Exception:
with allure.step("用例执行失败信息"):
allure.attach(self.response.content, "执行失败信息", allure.attachment_type.JSON)
pytest.fail(f"用例执行失败状态码: {self.response.status_code},请求url:{case['url']}")
例如:登录调用统一执行每一步用例
class Test_adapter(Test_api):
@pytest.mark.parametrize('cases',load_test_data())
def test_api(self,cases):
self.api_case(cases)
断言使用
#返回数据转成json格式 其实是一个字典
context_dict = self.response.json()
assert response.status_code == 200
assert response.方法
两种方式实现测试用例
第一种: 一个测试用例一个文件
getadapterlist:
- title: 获取控制单元列表
des: 获取所有控制单元的基础信息
cases:
- name: 获取所有控制单元的基础信息
url: http://172.11.32.82:8802/api/InstitutionMng/GetAdapterList/
method: POST
headers:
Content-Type: application/json
payload:
{"pageIndex":1,"pageSize":10,"queryModel":{"Id":"","Name":"","StationId":[],"CompanyId":""}}
一个py文件调用公共方法
import allure
import pytest
import requests
import yaml
from common.run_api import Test_api
def load_test_data():
with open('F:/EMS2.1.0/testdata/Institutionmng/adapter/test_adapter.yaml', 'r', encoding='utf-8') as file:
test_data = yaml.safe_load(file)
return test_data['getadapterlist']
class Test_adapter(Test_api):
@pytest.mark.parametrize('cases',load_test_data())
def test_api(self,cases):
self.api_case(cases)
第二种: 一个测试用例多个文件
例如: 测试一个控制单元列表 一个yaml文件存放多条测试数据
getadapterinfoid:
- title: 控制单元详情
des: 获取控制单元详情
cases:
- name: 获取控制单元详情
url: http://172.11.32.82:8802/api/InstitutionMng/GetAdapterInfoById?adapterId=DAT012
method: GET
headers:
Content-Type: application/json
payload: {}
getadapterlist:
- title: 获取控制单元列表
des: 获取所有控制单元的基础信息
cases:
- name: 获取所有控制单元的基础信息
url: http://172.11.32.82:8802/api/InstitutionMng/GetAdapterList/
method: POST
headers:
Content-Type: application/json
payload:
{"pageIndex":1,"pageSize":10,"queryModel":{"Id":"","Name":"","StationId":[],"CompanyId":""}}
saveadapter:
- title: 保存控制单元信息
des: 保存控制单元的基础信息
cases:
- name: 保存控制单元的基础信息
url: http://172.11.32.82:8802/api/InstitutionMng/SaveAdapter/
method: POST
headers:
Content-Type: application/json
payload:
{"id":"","name":"12111","stationId":"TC004","stationName":"天顺五股泉风储电站"}
deleteadapter:
- title: 删除控制单元信息
des: 删除控制单元的基础信息
cases:
- name: 删除控制单元信息
url: http://172.11.32.82:8802/api/InstitutionMng/DeleteAdapter?ids=DAT013
method: GET
headers:
Content-Type: application/json
payload:
{}
exportadapter:
- title: 导出控制单元信息
des: 导出控制单元的基础信息
cases:
- name: 导出控制单元信息
url: http://172.11.32.82:8802/api/InstitutionMng/ExportAdapterData/
method: POST
headers:
Content-Type: application/json
payload:
{"pageIndex":1,"pageSize":10,"queryModel":{"Id":"","Name":"","StationId":[],"CompanyId":""}}
一个py文件参数化的方式调用公共方法
import allure
import pytest
import requests
import yaml
import os
def load_test_data(case_type):
test_files = [
'F:/EMS2.1.0/testdata/Institutionmng/adapter/test_adapter.yaml',
'F:/EMS2.1.0/testdata/Institutionmng/company/test_company.yaml',
'F:/EMS2.1.0/testdata/Institutionmng/station/test_station.yaml',
]
test_data = {}
for file in test_files:
with open(file, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
test_data.update(data)
#print(test_data)
return test_data[case_type]
@allure.feature('查看控制单元列表')
class TestAdapter(TestApi):
@allure.story('测试用例')
@pytest.mark.parametrize('cases', [
pytest.param(load_test_data('getadapterlist'), id='获取所有控制单元的基础信息'),
pytest.param(load_test_data('getadapterlist1'), id='获取所有控制单元的基础信息url地址错误'),
pytest.param(load_test_data('getadapterlist2'), id='获取所有控制单元的基础信息分页错误'),
pytest.param(load_test_data('getadapterinfoid'),id='获取控制单元的详细信息')
])
#另一种参数写法
#@pytest.mark.parametrize('cases',[load_test_data('getadapterlist'),load_test_data('getadapterlist1'),load_test_data('getadapterlist2')])
def test_api(self, cases):
self.api_case(cases)
实现多个参数化的形式实现的多个用例执行