allure钩子函数 && selenium 截图的四种方式 && allure集成错误截图报告
1.selenium 截图的四种方式
1. save_sreenshoot # 一般不用 坑多 2. get_sreenshoot_as_file # 保存网页截图 3. get_sreenshoot_as_png #获取二进制数据流 4.get_sreenshoot_as_base64 # base64编码原始数据
2.allure的钩子函数
Hooks 函数获取用例执行结果(pytest_runtest_makereport)
看一下ruuner的源码 pytest执行测试原理 setup call teardown
_pytest.runner
from _pytest import runner # 对应源码 def pytest_runtest_makereport(item, call): """ return a :py:class:`_pytest.runner.TestReport` object for the given :py:class:`pytest.Item` and :py:class:`_pytest.runner.CallInfo`. """
这里item是测试用例,call是测试步骤,具体执行过程如下:
- 先执行when='setup' 返回setup 的执行结果
- 然后执行when='call' 返回call 的执行结果
- 最后执行when='teardown'返回teardown 的执行结果
运行实例
conftest.py 写 pytest_runtest_makereport 内容,打印运行过程和运行结果
# conftest.py import pytest @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_makereport(item, call): print('------------------------------------') # 获取钩子方法的调用结果 out = yield print('用例执行结果', out) # 3. 从钩子方法的调用结果中获取测试报告 report = out.get_result() print('测试报告:%s' % report) print('步骤:%s' % report.when) print('nodeid:%s' % report.nodeid) print('description:%s' % str(item.function.__doc__)) print(('运行结果: %s' % report.outcome))
test_a.py写一个简单的用例
def test_a(): '''用例描述:test_a''' print("上海-悠悠")
运行结果如下
D:\soft\code\pytest_jenkins_demo\demo>pytest -s ============================= test session starts ============================= platform win32 -- Python 3.6.0, pytest-4.5.0, py-1.5.4, pluggy-0.13.1 rootdir: D:\demo plugins: html-1.19.0, collected 1 item test_a.py ------------------------------------ 用例执行结果 <pluggy.callers._Result object at 0x0000027C547332B0> 测试报告:<TestReport 'test_a.py::test_a' when='setup' outcome='passed'> 步骤:setup nodeid:test_a.py::test_a description:用例描述:test_a 运行结果: passed 上海-悠悠 ------------------------------------ 用例执行结果 <pluggy.callers._Result object at 0x0000027C547332B0> 测试报告:<TestReport 'test_a.py::test_a' when='call' outcome='passed'> 步骤:call nodeid:test_a.py::test_a description:用例描述:test_a 运行结果: passed .------------------------------------ 用例执行结果 <pluggy.callers._Result object at 0x0000027C54750A20> 测试报告:<TestReport 'test_a.py::test_a' when='teardown' outcome='passed'> 步骤:teardown nodeid:test_a.py::test_a description:用例描述:test_a 运行结果: passed ========================== 1 passed in 0.06 seconds ===========================
从运行结果可以看出,运行用例的过程会经历三个阶段:setup-call-teardown,每个阶段都会返回的 Result 对象和 TestReport 对象,以及对象属性。
setup和teardown上面的用例默认都没有,结果都是passed
setup和teardown
给用例写个fixture增加用例的前置和后置操作,conftest.py内容如下
import pytest @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_makereport(item, call): print('------------------------------------') # 获取钩子方法的调用结果 out = yield print('用例执行结果', out) # 3. 从钩子方法的调用结果中获取测试报告 report = out.get_result() print('测试报告:%s' % report) print('步骤:%s' % report.when) print('nodeid:%s' % report.nodeid) print('description:%s' % str(item.function.__doc__)) print(('运行结果: %s' % report.outcome)) @pytest.fixture(scope="session", autouse=True) def fix_a(): print("setup 前置操作") yield print("teardown 后置操作")
运行结果如下
setup失败情况
当setup执行失败了,setup的执行结果的failed,后面的call用例和teardown都不会执行了
此时用例的状态是:error, 也就是用例(call)都还没开始执行,就异常了
call失败情况
如果setup正常执行,但是测试用例call失败了
@pytest.fixture(scope="session", autouse=True) def fix_a(): print("setup 前置操作") yield print("teardown 后置操作")
test_a.py用例
def test_a(): '''用例描述:test_a''' print("上海-悠悠") assert 1==0
那么此时运行的结果就是failed
teardown失败了
如果setup正常执行,测试用例call正常执行,teardown失败了,这种情况
@pytest.fixture(scope="session", autouse=True) def fix_a(): print("setup 前置操作") yield print("teardown 后置操作") raise Exception("teardown 失败了")
teat_a.py用例
def test_a(): '''用例描述:test_a''' print("上海-悠悠")
最终统计的结果: 1 passed, 1 error in 0.16 seconds
只获取call的结果
我们在写用例的时候,如果保证setup和teardown不报错情况,只关注测试用例本身的运行结果,前面的 pytest_runtest_makereport 钩子方法执行了三次。
可以加个判断:if report.when == "call"
import pytest from _pytest import runner ''' # 对应源码 def pytest_runtest_makereport(item, call): """ return a :py:class:`_pytest.runner.TestReport` object for the given :py:class:`pytest.Item` and :py:class:`_pytest.runner.CallInfo`. """ ''' @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_makereport(item, call): print('------------------------------------') # 获取钩子方法的调用结果 out = yield # print('用例执行结果', out) # 3. 从钩子方法的调用结果中获取测试报告 report = out.get_result() if report.when == "call": print('测试报告:%s' % report) print('步骤:%s' % report.when) print('nodeid:%s' % report.nodeid) print('description:%s' % str(item.function.__doc__)) print(('运行结果: %s' % report.outcome)) @pytest.fixture(scope="session", autouse=True) def fix_a(): print("setup 前置操作") yield print("teardown 后置操作")
运行结果
3.allure报告集成错误截图
需要使用conftest.py文件,conftest.py需要存在在测试目录中,文件名不能变更,可以根据模块创建层级嵌套。
具体参照pytest的官方文档
@pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): ''' hook pytest失败 :param item: :param call: :return: ''' # execute all other hooks to obtain the report object outcome = yield rep = outcome.get_result() # we only look at actual failing test calls, not setup/teardown if rep.when == "call" and rep.failed: mode = "a" if os.path.exists("failures") else "w" with open("failures", mode) as f: # let's also access a fixture for the fun of it if "tmpdir" in item.fixturenames: extra = " (%s)" % item.funcargs["tmpdir"] else: extra = "" f.write(rep.nodeid + extra + "\n") # pic_info = adb_screen_shot() with allure.step('添加失败截图...'): allure.attach(driver.get_screenshot_as_png(), "失败截图", allure.attachment_type.PNG)
4.Pytest+Allure+Selenuim+异常截屏+Allure日志
需要使用conftest.py文件,conftest.py需要存在在测试目录中,文件名不能变更,可以根据模块创建层级嵌套。具体参照pytest的官方文档