0


基于python的PYtest+PO+Selenium+DDT+Allure web自动化测试框架

项目的框架讲解:

图片大致已经将框架讲解清楚,下面就是单独的对这个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

提供了四种作用范围:

  1. function: 默认值,表示每个测试函数都会调用一次这个 fixture。
  2. class: 表示每个测试类调用一次这个 fixture。
  3. module: 表示每个测试模块调用一次这个 fixture。
  4. 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 可以方便地管理测试前的准备工作和测试后的清理工作(例如,打开和关闭浏览器)。这样,你可以确保每次测试运行在一致的状态下。

标签: python pytest selenium

本文转载自: https://blog.csdn.net/weixin_63213632/article/details/143189085
版权归原作者 Zpb185 所有, 如有侵权,请联系我们删除。

“基于python的PYtest+PO+Selenium+DDT+Allure web自动化测试框架”的评论:

还没有评论