项目的框架讲解:
图片大致已经将框架讲解清楚,下面就是单独的对这个conftest的这个类说一下,
前期配置:
下载好对应的webdriver之后,配置好对应的浏览器版本,编写一个test代码,查看是否可以正常打开浏览器。
首先把测试运行的程序选出pytest,这种一般都是pycharm自带的,不需要去下载。
需要安装的包:
allure-pytest==2.13.5 用来生成allure测试报告
allure-python-commons==2.13.5
logging 用来生成日志
os 用来生成文件路径
sqlite3 用来连接数据库
selenium 用来打开浏览器
Allure: 用来读取生成的测试报告,这个得去下载安装,并且配置到环境变量里。
确保Allure命令行工具已经正确安装并配置后,重新运行生成报告的命令:
(如果这些文件夹下面都有文件了直接删除了,再重新生成就好了)
pytest --alluredir=C:\Users\12165\Desktop\auto-ui\allure_data\allure_results test_login_register.py
allure generate C:\Users\12165\Desktop\auto-ui\allure_data\allure_results -o C:\Users\12165\Desktop\auto-ui\allure_data\allure_report --clean
allure open C:\Users\12165\Desktop\auto-ui\allure_data\allure_report
allure generate C:\Users\12165\Desktop\auto-ui\allure_data\allure_results -o C:\Users\12165\Desktop\auto-ui\allure_data\allure_report --clean
然后打开生成的报告:
allure open C:\Users\12165\Desktop\auto-ui\allure_data\allure_report
文件创建规则:
在创建测试用例类的时候记得选择python的单元测试,并且确保你的文件名是以
test_
开头,或者类名和方法名是以
Test
和
test_
开头,因为
pytest
默认只识别这些模式。如果你的文件名字不是这个名字则无法被运行。
断言
pytest
使用 Python 内置的
assert
语句,并增强了其错误报告功能,当断言失败时,
pytest
会提供详细的信息来帮助你调试,怎么理解呢?直接理解为这个语句就是来决定你这个测试用例的失败
与否的,如果不满足断言语句则失败,反则成功。
def test_example():
x = 5
y = 10
assert x + y == 20 # 这是一个错误的断言
def test_custom_message():
x = 5
y = 10
assert x + y == 20, "x 加 y 的结果应该是 20"
这里讲一下语法(TTD):
@pytest.mark.parametrize
是
pytest
中一个非常有用的装饰器,用于参数化测试。它允许你使用不同的参数集多次运行同一个测试函数,从而减少重复代码,提高测试的覆盖率和效率。这串修饰代码放在你要循环的测试函数之前即可完成循环,没有被修饰到的函数不会被循环到的。
@pytest.mark.parametrize('username,password,result',[
('zhang','1234','欢迎您:zhang | 退出'),
('123123','1234','用户名错误'),
('zhang','1235','密码错误')
])
还有一个问题,你循环的时候如果不设置ids的名字这个代码那么你的测试用例的面子就是乱码,因此给元组中每个被循环到的元素添加对应的用例名称。
@pytest.mark.parametrize('username,password,result',[
('zhang','1234','欢迎您:zhang | 退出'),
('123123','1234','用户名错误'),
('zhang','1235','密码错误')
],ids=('test_shopping_mall_001',
'test_shopping_mall_002',
'test_shopping_mall_003'))
def test_shopping_mall(self,username,password,result,login):
driver=login
driver.get(ENV.url)
driver.find_element(By.XPATH, "//a[contains(text(),'登录')]").click()
driver.find_element(By.XPATH, "//input[@placeholder='请输入用户名']").send_keys(username)
driver.find_element(By.XPATH, "//input[@placeholder='请输入密码']").send_keys(password)
driver.find_element(By.XPATH, "//input[@value='登录']").click()
if '欢迎您' in result:
text = driver.find_element(By.XPATH, "//div[@class='login_btn fl']").text
driver.find_element(By.XPATH, "//a[contains(text(),'退出')]").click()
assert text == result
if '用户名错误' in result:
text = driver.find_element(By.XPATH, "//div[@class='user_error']").text
assert text == '用户名错误'
if '密码错误' in result:
text = driver.find_element(By.XPATH, "//div[@class='pwd_error']").text
assert text == '密码错误'
**语法之@pytest.hookimpl(tryfirst=True, hookwrapper=True)
示例:这个语法是用在conftest里的,**
pytest.hookimpl 来定义一个 pytest 钩子方法 pytest_runtest_makereport,这个钩子方法在每个测试用例执行完毕后都会被调用,用于生成并处理测试报告,不需要引入到测试用例的。
这里为什么要用到out=yield呢?因为是用来接受这个yield的状态的,例如这个测试用例没有通过那么out.get_result()返回的就是false,那么如果返回的是fasle那么我就截图,啥时候截图 肯定是测试用例执行结束的才调用截图,所以
不一定会截取到失败的那个点,例如我一个用例是点a点b再点c点b的时候就报错了,但是这个用例还没完,还会点c,所以你截图的页面就是c,所以不一定是真的错误的页面
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""
pytest 失败后执行
:param item: 测试用例
:param call: 测试步骤
:return:
"""
out = yield
result = out.get_result()
log.info(f"test report:{result}")
log.info(f"execution time-consuming:{round(call.duration, 2)} second")
# if result.failed and result.when == 'call':
if result.failed:
try:
log.info('error.screenshot.')
driver.allure_save_screenshot('error_screenshot')
except Exception as e:
log.error(e)
pass
**这个语法也是用在conftest里的:**
那么这串代码是什么意思呢?
@pytest.fixture(scope="class")
是
pytest
中 fixture 的一种使用方式,用于设置 fixture 的作用范围(scope)。fixture 是一种在测试运行之前和之后执行特定代码的机制,提供了一种优雅、灵活的方式来准备和清理测试环境。下面我们详细讲解一下这个语法。
scope
参数
scope
参数用于定义 fixture 的作用范围(即生命周期),
pytest
提供了四种作用范围:
function
: 默认值,表示每个测试函数都会调用一次这个 fixture。class
: 表示每个测试类调用一次这个 fixture。module
: 表示每个测试模块调用一次这个 fixture。session
: 表示整个测试会话期间仅调用一次这个 fixture。
@pytest.fixture(scope="class")
当你使用
@pytest.fixture(scope="class")
时,表示这个 fixture 会在每个测试类开始时调用一次,并在类结束时执行清理工作。在一个类中的所有测试方法共享同一个 fixture 实例。
@pytest.fixture(scope="class")
def open_homepage():
global driver
driver = HomePage()
driver.get(ENV.url)
yield driver
driver.quit()
那么如果我的测试类要调用到这个方法应该怎么做?
就像这样,就算你这个方法中没有用到open_homepage,你只需把它作为函数的参数放在那里一样会调用到open_page这个方法。**
pen_homepage
作为参数**: 即使你没有显式地使用
open_homepage
变量,只要它出现在参数列表中,
pytest
会自动调用
@allure.feature('登录注册')
@allure.story('注册')
def test_shopping_mall_005(self, open_homepage):
user_name = ENV.user_name
pwd = 'test_mall_004'
cpwd = 'test_mall_004'
email = '[email protected]'
driver = open_homepage
Event.event_register(driver, user_name, pwd, cpwd, email)
# 检查错误提示
# 注意该定位方式:直接查找class='error_tip'有4个,通过其兄弟节点再查找,能精确定位到
text = driver.get_text((By.XPATH, "//input[@id='user_name']//..//*[@class='error_tip']"))
assert text == '用户名已经存在'
关于allure语法
@allure.step('注册')
- 解释:
@allure.step
用于标记测试步骤。 - 参数:字符串参数
'注册'
描述了这个步骤的名称,显示在报告中。 - 用法:可以用于函数或方法,表示这个函数或方法是测试流程中的一个步骤。
- 一般用在po层中,用来给动作和事件形容,在生成的allure报告就可以更好的理解。
@allure.feature('登录注册')
- 解释:
@allure.feature
用于将测试用例分组到某个功能模块中。 - 参数:字符串参数
'登录注册'
描述了这个功能模块的名称。 - 用法:通常用于标记测试类或测试方法,表示这些测试用例属于某个特定的功能模块。
- 用于修饰这个测试用例的功能,在书写测试用例时使用,在testcase层,一个测试用例可以被多个形容词修饰。
@allure.story('退出')
- 解释:
@allure.story
用于将测试用例进一步分类到某个用户故事或子功能中。 - 参数:字符串参数
'退出'
描述了这个用户故事或子功能的名称。 - 用法:通常用于标记测试类或测试方法,表示这些测试用例属于某个特定的用户故事或子功能。
- 用于修饰测试用例,描述这个测试用例的目的。
**关于类的构造函数和析构函数分别是
构造函数跟Java的一样,你这里直接把self看成this就可以了。**__init__(self,value) 这个就是这个类的构造函数 和 __del__(self)是类的析构函数,在对象被垃圾回收时自动调用。 它用于执行清理操作,比如释放资源或关闭文件。
说人话就是只要调用到这个类,就会自动执行这两个方法
看一下Java和python的对比你就知道了:
Python语言
class MyClass:
def __init__(self, value): #构造函数
self.value = value # 通过 self 引用实例属性
def display(self):
print(self.value) # 通过 self 引用实例方法和属性
obj = MyClass(10)
obj.display() # 输出: 10
Java语言
public class MyClass {
private int value;
public MyClass(int value) { //构造函数
this.value = value; // 通过 this 引用实例属性
}
public void display() {
System.out.println(this.value); // 通过 this 引用实例方法和属性
}
public static void main(String[] args) {
MyClass obj = new MyClass(10);
obj.display(); // 输出: 10
}
}
奇思妙想:
换成MySQL数据库,
困惑的点就是我在编写测试的自动化框架的po层这里我会把一些事件抽象出来 例如登录,那我在写testcase层的时候写测试用例的时候又会用到conftest.py这个文件,那这个文件里通常也是存放一些像登录一样的事件,我像请问一下,同样都是登录这种事件,例如我在测试用例用到的时候我直接调用po层封装好的event事件就好了,为什么我又得在conftest.py写一个登录的事件呢?
我的建议是你对比一下,两个都是登录显然conftest封装的更好,更进一节,所以就算写好了事件,能在conftest封装一下是最好的,虽然在测试用例中直接调用
Event.event_login
也能实现登录功能,但使用 fixtures 提供了更多的灵活性、可维护性和组织性。特别是当你的测试套件变得越来越大时,这些优势将变得更加明显。如果你的项目处于初级阶段或规模较小,可以暂时直接调用
Event.event_login
方法,随着项目的发展,你可以逐步引入 fixtures 来优化和管理测试代码。统一管理测试前后的状态:前置和后置动作:fixture 可以方便地管理测试前的准备工作和测试后的清理工作(例如,打开和关闭浏览器)。这样,你可以确保每次测试运行在一致的状态下。
版权归原作者 Zpb185 所有, 如有侵权,请联系我们删除。