https://www.jianshu.com/p/9a03984612c1?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-timeline&from=timeline&isappinstalled=0
Project structure
用例层(测试用例)
|
Fixtures层(业务流程)
|
PageObject层
|
Utils实用方法层
Use pytest-selenium
Basics
# test_baidu.py
def test_baidu(selenium): selenium.get('https://www.baidu.com') selenium.find_element_by_id('kw').send_keys('简书 韩志超') selenium.find_element_by_id('su').click()
run
$ pytest test_baidu.py --driver=chrome
Or configurations of the pytest.ini
[pytest]
addopts = --driver=chrome
Use chrome options
# conftest.py
import pytest
@pytest.fixture
def chrome_options(chrome_options): # 覆盖原有chrome_options chrome_options.add_argument('--start-maximized') # chrome_options.add_argument('--headless') return chrome_options
Page Object层
The basic model
# baidu_page.py
class BaiduPage(object): search_ipt_loc = ('id', 'kw') search_btn_loc = ('id', 'su') def __init__(self, driver): self.driver = driver def input_search_keyword(self, text): self.driver.find_element(*self.search_ipt_loc).send_keys(text) def click_search_button(self): self.driver.find_element(*self.search_btn_loc).click() def search(self, text): self.input_search_keyword(text) self.click_search_button()
Call the method:
# test_baidu_page.py
from baidu_page import BaiduPage
def test_baidu_page(selenium): baidu = BaiduPage(selenium) baidu.search('简书 韩志超')
Use the page base class
# pages/base_page.py
class BasePage(object): def __init__(self, driver): self.driver = driver def input(self, element_loc, text): element = self.driver.find_element(*element_loc) element.clear() element.send_keys(text) def click(self, element_loc): self.driver.find_element(*element_loc).click()
# pages/baidu_page.py
from pages.base_page import BasePage
class BaiduPage(BasePage): search_ipt_loc = ('id', 'kw') search_btn_loc = ('id', 'su') def input_search_keyword(self, text): self.input(self.search_ipt_loc, text) def click_search_button(self): self.click(self.search_btn_loc) def search(self, text): self.input_search_keyword(text) self.click_search_button()
Fixtures business layer
# conftest.py
import pytest
from pages.baidu_page import BaiduPage()
@pytest.fixture(scope='session') def baidu_page(selenium): return BaiduPage(selenium)
EXAMPLE layer with
# test_baidu_page2.py
def test_baidu_page(baidu_page): baidu_page.search('简书 韩志超') assert '韩志超' in baidu.driver.title
Progressive steps
Click Add Product menu> - - the interdependence of cases should not be used if the part has the same business process use cases, such as the need to open the login page -> Login> Add to enter the product page
is not recommended for use in the following manner, and make the order carried out.
def test_login():
...
def test_click_menu(): ... def test_add_goods(): ...
Recommended common encapsulation step, each call may be used to implement step Fixture progressive method, the following example.
# conftest.py
import pytest
from pages.login_page import LoginPage
from pages.menu_page import MenuPage from pages.add_goods_page import AddGoodsPage @pytest.fixture(scope='session') def login_page(selenium): return LoginPage(selenium) @pytest.fixture(scope='session') def menu_page(selenium, login_page): """登录后返回菜单页面""" login_page.login('默认用户名', '默认密码') # 也可以从数据文件或环境变量中读取 return MenuPage(selenium) @pytest.fixture(scope='session') def add_goods_page(selenium, menu_page): """从MenuPage跳到添加商品页面""" menu_page.click_menu('商品管理', '添加新商品') return AddGoodsPage(selenium)
# test_ecshop.py
def test_login(login_page): login_page.login('测试用户名', '测试密码') assert login_page.get_login_fail_msg() is None def test_add_goods(add_goods_page): add_goods_page.input_goods_name('dell电脑') add_goods_page.input_goods_category("电脑") add_goods_page.input_goods_price('3999') add_goods_page.submit() assert add_goods_page.check_success_tip() is True
Using Log
The necessary output information in the project can help us show some intermediate results and rapid positioning of the test procedure, although Pytest framework can automatically capture print information and output screen or report, and more standardized use logging records and log output .
Compared print, logging module may record information graded.
Log level
Practical Method layer, a page object layer, Fixture business layer, with layer embodiment can be used directly to output log logging, use.
# test_logging.py
import logging
def test_logging(): logging.debug('调试信息') logging.info('步骤信息') logging.warning('警告信息,一般可以继续进行') logging.error('出错信息') try: assert 0 except Exception as ex: logging.exception(ex) # 多行异常追溯信息,Error级别 logging.critical("严重出错信息")
Use pytest run there will be no log information because Pytest default display only WARNING above grade log in information in error.
To turn on the screen in real-time log, display and modify the log level.
Log等级: NOTSET < DEBUG < INFO < WARNING(=WARN) < ERROR < CRITICAL
# pytest.ini
[pytest]
log_cli=True
log_cli_level=INFO
Run pytest test_logging.py, view the results:
--------------------------------------------- live log call ----------------------------------------------
INFO root:test_logging.py:5 步骤信息
WARNING root:test_logging.py:6 警告信息,一般可以继续进行
ERROR root:test_logging.py:7 出错信息
ERROR root:test_logging.py:11 assert 0
Traceback (most recent call last):
File "/Users/apple/Desktop/demo/test_logging.py", line 9, in test_logging
assert 0
AssertionError: assert 0
CRITICAL root:test_logging.py:12 严重出错信息
Since the log level is INFO level settings, so debug log is not output.
For the different layers Use log level, a method may be practical level logging debug output layer, assembling the file path, the file read data, sql executed, sql query result and the like.
In the output layer PageObject info level logging, perform an action such as a page, and the like.
Fixtures layer and output layer may use cases necessary info, warning, or error level information as needed.
Log Format
The default log format does not display the execution time, we can also customize log output format.
# pytest.ini
...
log_cli_format=%(asctime)s %(levelname)s %(message)s
log_cli_date_format=%Y-%m-%d %H:%M:%S
%(asctime)s
Represents the time, the default forSat Jan 13 21:56:34 2018
this format, we can use log_cli_date_format to specify the time format.%(levelname)s
The level of representation of this Article logs%(message)s
Concrete output
Pytest test_logging.py run again, displayed in the following format:
--------------------------------------------- live log call ----------------------------------------------
2019-11-06 21:44:50 INFO 步骤信息
2019-11-06 21:44:50 WARNING 警告信息,一般可以继续进行
2019-11-06 21:44:50 ERROR 出错信息
2019-11-06 21:44:50 ERROR assert 0
Traceback (most recent call last):
File "/Users/apple/Desktop/demo/test_logging.py", line 9, in test_logging
assert 0
AssertionError: assert 0
2019-11-06 21:44:50 CRITICAL 严重出错信息
More Log Display Options
- % (Levelno) s: printing log level values
- % (Pathname) s: print path currently executing program, in fact, sys.argv [0]
- % (Filename) s: Print the name of the currently executing program
- % (FuncName) s: printing log current function
- % (Lineno) d: the current line number of the printing log
- % (Thread) d: Print thread ID
- % (ThreadName) s: print thread name
- % (Process) d: Print Process ID
Log output to a file
Add the following configuration in pytest.ini
...
log_file = logs/pytest.log
log_file_level = debug
log_file_format = %(asctime)s %(levelname)s %(message)s
log_file_date_format = %Y-%m-%d %H:%M:%S
log_file output file path, the file is input to the log level, format, date format to be separately provided.
Unfortunately, the log output to a file each time you run a cover, do not support the append mode.
Use Hooks
Hooks can be changed using the running processes Pytest, Hooks method generally written in conftest.py, a fixed name.
Pytest of Hooks method is divided into six levels:
- Method when the guide hook
- Initialization of the hook method
- The method of collecting hook when cases of use
- Hook method of test runs
- Hook method when generating reports
- Hook method when breakpoint debugging
Hooks method Pytest complete API, you can refer to: the API Reference hook -04- (Hooks)
Change setting
The following method demonstrates the dynamic test report generation name.
# conftest.py
import os
from datetime import datetime
def pytest_configure(config): """Pytest初始化时配置方法""" if config.getoption('htmlpath'): # 如果传了--html参数 now = datetime.now().strftime('%Y%m%d_%H%M%S') config.option.htmlpath = os.path.join(config.rootdir, 'reports', f'report_{now}.html')
Whether the above example, the user --html pass what each run, the project reports will be in the directory, generate report_运行时间.html
a new reporting format.
Hook pytest_configure is a fixed method when Pytest guide, we again this method can be achieved when Pytest initial configuration, put our methods to be performed (methods become hook) or in a use case conftest.py file.
config parameters are fixed parameters of the method, the plug comprising at Pytest initialization, all of the command line parameters, INI project configuration.
You can use Python's introspection method Print (config., Dict ) to view the config object's properties.
Typically, by config.getoption - to get the value of the command line parameter item ( 'html'). Use config.getini ( 'log_file') can get the value of the configuration item pytest.ini file.
Add custom options and configurations
Suppose we want to send Email to achieve a complete run function.
We customize a command-line parameter entry --send-email, does not require a parameter value. When a user operation to bring the parameters, we send a report, not hair without running the following format:
pytest test_cases/ --html=report.html --send-email
Here, the general should cooperate to Mr. --html report.
Since Pytest itself does not --send-email this parameter, we need to be added by Hooks method.
# conftest.py
def pytest_addoption(parser): """Pytest初始化时添加选项的方法""" parser.addoption("--send-email", action="store_true", help="send email with test report")
In addition, we also need to send an email message subject, body, recipients and other configuration information. We can put this information in the configuration to pytest.ini, such as:
# pytest.ini
...
email_subject = Test Report
email_receivers = superhin@126.com,[email protected]
email_body = Hi,all\n, Please check the attachment for the Test Report.
It should be noted that the custom configuration options need to register in order to use, registration as follows.
# conftest.py
def pytest_addoption(parser): ... parser.addini('email_subject', help='test report email subject') parser.addini('email_receivers', help='test report email receivers') parser.addini('email_body', help='test report email body')
Send Email function realization
Earlier we just add the Email configuration and operating parameters, we Hook method when generating a report, add transmission parameters according to the Email function, the following example.
from utils.notify import Email
# conftest.py
def pytest_terminal_summary(config): """Pytest生成报告时的命令行报告运行总结方法""" send_email = config.getoption("--send-email") email_receivers = config.getini('email_receivers').split(',') if send_email is True and email_receivers: report_path = config.getoption('htmlpath') email_subject = config.getini('email_subject') or 'TestReport' email_body = config.getini('email_body') or 'Hi' if email_receivers: Email().send(email_subject, email_receivers, email_body, report_path)
Use allure-pytest
allure style is a very rich reporting framework.
Installation: pip install allure-pytest
![](http://upload-images.jianshu.io/upload_images/7575721-bc98f5350e5cc62a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
Reference documents: https://docs.qameta.io/allure/#_installing_a_commandline
Allure report contains the following pieces:
- Overview: Overview
- Categories: Failed use case classification
- Suites: measuring gloves member, corresponding to the test class pytest
- Graphs: graphs, reports by state with an overall embodiment, different severity levels and marked execution time distribution.
- Timeline: Timeline of execution
- Behaviors: BDD driving behavior patterns, by Epic, function, scenes user
to mark and organizations use cases. - Pachages: according to the package directory to see the use case
Example labeled with
pytest-allure embodiment can automatically recognize the failure by pytest through, skip, xfail state and other reasons, and to provide more additional markers, the information to improve the use cases.
Further, allure provides many additional embodiments numerals or tissue with supplemental information with embodiments.
Mark test procedure
@allure.step('')
@allure.step
def func(): pass
When this method is invoked with the embodiment, the report will be regarded as a step, according to the call nesting relationship recognition step.
To add extra information use case
Add attachments
- @allure.attach.file('./data/totally_open_source_kitten.png', attachment_type=allure.attachment_type.PNG)
Add a title and description
- @allure.description('')
- @allure.description_html('')
- @allure.title("This test has a custom title")
Add links, issue links, link the use case
- @allure.link('http://...')
- @ Allure.issue ( 'B140', 'Bug description')
- @ Allure.testcase ( 'http: // ...', 'example name')
Example BDD mode tissues
- @allure.epics('')
- @allure.feature('')
- @allure.story('')
- @allure.step('')
You can run as story or feature
- --allure-epics
- --allure-features
- --allure-stories
Severity mark
- @allure.severity(allure.severity_level.TRIVIAL)
- @allure.severity(allure.severity_level.NORMAL)
- @allure.severity(allure.severity_level.CRITICAL)
Select the priority of execution in the following ways
--allure-severities normal,critical
Generate reports allure
pytest --alluredir=报告文件夹路径
After running the folder will have a xml format of the report file.
This report documents the use of plug-in to resolve directly jenkinz.
If you want to see local html format reports, you need to install allure.
installation method:
- Mac: brew install allure
- CentOS: yum install allure
- Windows: Download , unzip outside download, go to the bin directory, you can use allure.bat. Use, generate html reports:
allure generate 生成allure报告的文件夹
Windows can be used allure.bat generate in the bin directory allure of ...
Or by launching the report static service:
allure serve 生成allure报告的文件夹
It will automatically pop up a browser to access report generation.
Pytest actual testing framework APP
APP and belong to the same Web UI layer, we can use the same hierarchy contains Page Object pattern. The difference is that we need to customize driver this Fixture.
# conftest.py
import pytest
from appium import webdriver
@pytest.fixture(scope='session') def driver(): caps = { "platformName": "Android", "platformVersion": "5.1.1", "deviceName": "127.0.0.1:62001", "appPackage": "com.lqr.wechat", "appActivity": "com.lqr.wechat.ui.activity.SplashActivity", "unicodeKeyboard": True, "resetKeyboard": True, "autoLaunch": False } driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', caps) driver.implicitly_wait(10) yield driver driver.quit()
Fixture and other driver or incorporated directly with the embodiments as a parameter can be used.
# test_weixin.py
def test_weixin_login(driver): driver.find_element_by_xpath('//*[@text="登录"]').click() ...
Use pytest-variables
Installation by pip install pytest-variables
If we need to specify the device configuration and service Appium address used at runtime, we can put these configurations wrote a JSON file, then use pytest-variables plug load these variables.
caps.json contents of the file:
{
"caps": {
"platformName": "Android",
"platformVersion": "5.1.1", "deviceName": "127.0.0.1:62001", "appPackage": "com.lqr.wechat", "appActivity": "com.lqr.wechat.ui.activity.SplashActivity", "unicodeKeyboard": true, "resetKeyboard": true, "autoLaunch": false }, "server": "http://localhost:4723/wd/hub" }
Fixtures used:
# conftest.py
...
@pytest.fixture(scope='session')
def driver(variables): caps = variables['caps'] server = variables['server'] driver = webdriver.Remote(server, caps) ...
Run method:
pytest test_weixin.py --variables caps.json
If you have multiple configurations can be caps.json format, save multiple profiles, load the specified configuration file to run. Operational parameters can also be added to the pytest.ini of addopts in.
Setup and cleanup
In order to ensure that each executing the use case does not affect each other, we can take each use case starts app are performed completely close the app, which belong to the use case method level Fixture method.
Meanwhile, since the execution time of the first embodiment is also called the app starts Fixture, where we need to set the default device is not connected to the automatic start app, i.e. caps configured autoLaunch = False.
Add the following Fixture method in conftest.py in:
# conftest.py
...
@pytest.fixture(scope='function', autouse=True)
def boot_close_app(driver): driver.boot_app() yield driver.close_app()
Page objects and other service encapsulation layer may Fixture Web reference model framework.
Author: HAN super
link: https: //www.jianshu.com/p/9a03984612c1
Source: Jane book
Jane book copyright reserved by the authors, are reproduced in any form, please contact the author to obtain authorization and indicate the source.