Web测试示例

场景分析

Web测试是一种 有状态测试 , 步骤之间往往有着巨大的依赖性和影响里,所以一个Web测试通常包含大量的测试步骤;

再加上Web测试通常使用Selenium或Playwright进行浏览器控制, 由于浏览器功能繁多,无法像requests那样的 统一传参接口 , 所以在执行测试步骤时,需要根据实时状态调用不同的方法、传递不同的参数;

此外,对浏览器进行自动化操作之后,其结果并不会进行返回,而是作用在Web页面的上, 所以不能直接对操作进行断言,而是需要再一次主动 从页面中获取结果 ,然后再进行断言,

由此可见,Web自动化测试的显著特点有:

  • 步骤数量多

  • 参数变化大

  • 断言不直接

备注

在实际项目中通常会对Selenium或Playwright进行二次封装, 以减少步骤和参数,从而降低一定难度。 但是受限于Web项目自身的特点,封装后的效率和难度依旧无法和API测试相比。

安装依赖

下面以Selenium为例

pip install selenium

Selenium 现在即可以自动下载驱动,又能够进行浏览器控制,所以只需要安装他一个即可。

设计用例

鉴于web测试的复杂度,可以先用python写出测试用例,然后再改写为yaml。

# test_web.py

from selenium import webdriver


def test_web():
    driver = webdriver.Chrome()
    driver.implicitly_wait(10)

    driver.get('https://www.baidu.com')
    driver.find_element('xpath','//*[@id="kw"]').send_keys("大红灯笼高高挂")
    driver.find_element('xpath','//*[@id="su"]').click()

    driver.get_screenshot_as_file('page.png')

    driver.quit()

(为了降低难度,该用例没有使用断言)

从Python用例可以看出,web测试用例步骤大致可分为3类:

  • 外部函数

  • driver中的成员方法

  • find_element返回值中的成员方法

参数类型也可分为3类:

  • 无参数

  • 位置参数

  • 关键字参数

根据python版本web测试用例,设计yaml版Web测试用例如下:

name: 使用百度搜索
steps:
  - get_webdriver:
  - implicitly_wait: 10

  - get: https://www.baidu.com

  - find_element:
      by: xpath
      value: '//*[@id="kw"]'
      action: # 此非selenium原生参数,需要二次处理
        send_keys: '大红灯笼高高挂'

  - find_element:
      by: xpath
      value: '//*[@id="su"]'
      action: # 此非selenium原生参数,需要二次处理
        click:

  - get_screenshot_as_file: page.png
  - quit:

实现钩子

接下来根据用例内容,实现hook,使得每个步骤都能够正确的执行

# conftest.py

from selenium import webdriver

from pytest_yaml import YamlItem


def pytest_yaml_run_step(item: YamlItem):
    step = item.current_step  # 从参数值中取出字典格式的测试步骤

    step_type = list(step.keys())[0]  # 从字典的key即为类型
    step_data = list(step.values())[0]  # 从字典的value即为步骤内容
    print(f"测试步骤:{item.current_step_no}, 步骤类型: {step_type}, 步骤内容: {step_data} ...", end="")

    action =None

    match step_type:  # 根据不同的步骤类型,匹配不同的测试动作
        case 'get_webdriver':
            # 启动浏览器,并将响应保存到item中,以便后续hook进行处理
            item.driver = webdriver.Chrome()
            print(f'浏览器启动成功!')
            return True

        case 'find_element':
            action = step_data.pop('action', None)

    func = getattr(item.driver, step_type)


    if not step_data:
        el =func()
    elif isinstance(step_data, dict):
        el = func(**step_data)
    else:
        el = func(step_data)

    if action and el:
        for action_name,action_args in action.items():
            action_func = getattr(el,action_name)
            if action_args:
                action_func(action_args)
            else:
                action_func()

    return True

执行结果如下

(.venv) C:\demo\pytest-yaml-demo>pytest
====================================== test session starts ======================================
platform win32 -- Python 3.12.0, pytest-8.3.4, pluggy-1.5.0
rootdir: C:\demo\pytest-yaml-demo
configfile: pytest.ini
plugins: yaml-1.1.0
collected 1 item

Web测试\test_web.yaml
DevTools listening on ws://127.0.0.1:60519/devtools/browser/363b32ff-33fa-428f-92f2-892a43376601
[6952:18404:1216/195012.865:ERROR:ssl_client_socket_impl.cc(879)] handshake failed; returned -1, SSL error code 1, net_error -101
.                                                                    [100%]

======================================= 1 passed in 5.03s =======================================

小结

至此,用于Web测试的yaml用例已成功实现。