How to save screenshots including test results, etc. when the test is finished in pytest - python

I want to save a screenshot when a test fails.
However, the screenshot name must include the information used in the test.
example
-> Currently my code is able to send the city I used in the test.
However, it does not operate according to success or failure.
How can I make the test receive a variable as well and act on success/failure?
(The file name I want: {city}{id}{pass/fail}.png
paris_testaccount01_pass.png)
test_example.py
class TesExample(BaseTest):
def test_example(self, capture_screenshot):
city = "paris"
id = "testaccount01"
# ~~ Skip test content ~~
capture_screenshot(self.driver, city)
BaseTest.py
import pytest
#pytest.mark.usefixtures("get_browser")
class BaseTest:
pass
conftest.py
#pytest.fixture()
def capture_screenshot():
def _capture_screenshot(get_browser, name):
driver = get_browser
driver.get_screenshot_as_file(f'../Screenshots/{name}.png')
return _capture_screenshot
return _capture_screenshot

If you use pytest-html for reporting and If you would take screenshot in case of fail, you can use following code in your conftest:
#mark.hookwrapper
def pytest_runtest_makereport(item, call):
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
extra = getattr(report, 'extra', [])
if report.when == 'call':
xfail_state = hasattr(report, 'wasxfail')
if (report.skipped and xfail_state) or (report.failed and not xfail_state):
mydriver = item.funcargs['driver']
screenshot = mydriver.get_screenshot_as_base64()
extra.append(pytest_html.extras.image(screenshot, ''))
report.extra = extra

Related

Screenshot is not getting generated on test failure even if the code for taking screenshot is correct in Selenium Python

I have attached a hook for taking screenshot whenever it encounters a failure and it will attach the screenshot to the report. But, on failure, the screenshot is not getting generated and on clicking the screenshot, the error is coming as 'Your file couldn’t be accessed. It may have been moved, edited, or deleted.
ERR_FILE_NOT_FOUND'
Attaching the hook which was able to capture screenshot successfully. But, now it is not getting generated.
#pytest.mark.hookwrapper
def pytest_runtest_makereport(item):
"""
Extends the PyTest Plugin to take and embed screenshot in html report, whenever test fails.
:param item:
"""
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
extra = getattr(report, 'extra', [])
if report.when == 'call' or report.when == "setup":
xfail = hasattr(report, 'wasxfail')
if (report.skipped and xfail) or (report.failed and not xfail):
file_name = report.nodeid.replace("::", "_") + ".png"
_capture_screenshot(file_name)
if file_name:
html = '<div><img src="%s" alt="screenshot" style="width:304px;height:228px;" ' \
'onclick="window.open(this.src)" align="right"/></div>' % file_name
extra.append(pytest_html.extras.html(html))
report.extra = extra
def _capture_screenshot(name):
driver.get_screenshot_as_file(name)
You did not add the path from where to take the screenshots I use something like this:
def remove_files():
download_path = r'E:\iollo\food-generation-ocb-automation-a36cc1ff0a57\scrennshot\**\*.png'
return download_path
Here I add a path to downloaded the screenshots in a folder and than I use the fixture to add the path in the report.
#pytest.hookimpl(hookwrapper=True)
#pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
extra = getattr(report, 'extra', [])
if report.when == 'call':
feature_request = item.funcargs['request']
xfail = hasattr(report, "wasxfail")
if (report.skipped and xfail) or (report.failed and not xfail):
feature_request = item.funcargs["request"]
driver = feature_request.getfixturevalue("setup")
timestamp = datetime.now().strftime('%H-%M-%S')
path = "/oalaCuBunatati"
ismount= os.path.ismount(path)
img_name = "name" + timestamp + ".png"
# img_path = os.path.join("D:\GIT\ocb-automation\scrennshot", img_name)
img_path = os.path.join(r'/oalaCuBunatati/scrennshot', img_name)
driver.save_screenshot(img_path)
if (report.skipped and xfail) or (report.failed and not xfail):
# only add additional html on failure
extra.append(pytest_html.extras.image(img_path))
extra.append(pytest_html.extras.html('<div>Additional HTML</div>'))
report.extra = extra
Maybe this will help you, in this way I use to generate the screenshots into html

How to pass fixture object to pytest_runtest_makereport?

We use the pytest library for automation test. We need to take screen shots for fail case. I want to use mydriver variable in pytest_runtest_makereport method to take screen shots with this variable for fail case. How can I do this?
Additional: We don't use the conftest.py file. Can we use the pytest_runtest_makereport method without conftest.py? We use just a sampleTest.py file.
sampleTest.py:
import time
from appium import webdriver
import pytest
#pytest.yield_fixture(scope="function")
def driver():
"""Setup for the test"""
desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '11.0'
desired_caps['deviceName'] = 'Samsung Galaxy A70'
# Since the app is already installed launching it using package and activity name
desired_caps['appPackage'] = 'com.example.mapkitbaseline'
desired_caps['appActivity'] = 'com.example.mapkitbaseline.ui.CreationMapActivity'
mydriver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
yield mydriver
mydriver.quit()
#pytest.mark.usefixtures("driver")
def test_func1(driver):
""""Testing the HMS MapKit demo app"""
driver.implicitly_wait(30)
time.sleep(5)
# Assert that MapKit->.ui.CreationMapActivity->btnMapCreation4 button text is equal to "suuportfragmentx"
elmnt = driver.find_element_by_id('com.example.mapkitbaseline:id/btnMapCreation4')
assert elmnt.get_attribute('text').lower() == "supportmapfragmentfffffffffx"
print(elmnt.get_attribute('text'))
#pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
extra = getattr(report, 'extra', [])
if report.when == "call":
xfail = hasattr(report, 'wasxfail')
if (report.skipped and xfail) or (report.failed and not xfail):
# I need to use mydriver variable this here. This code doesn't work.
screenshot = mydriver.get_screenshot_as_base64()
extra.append(pytest_html.extras.image(screenshot, ''))
# extra.append(pytest_html.extras.html('<style> #results-table{ position: relative; position:absolute !important;top:300px !important; left:100px !important; width: 50% !important; z-index: -1 !important;}</style>'))
report.extra = extra
You can access fixtures in item.funcargs.
if 'driver' in item.fixturenames: # Wrap in this
mydriver = item.funcargs['driver'] # Add this
screenshot = mydriver.get_screenshot_as_base64()
extra.append(pytest_html.extras.image(screenshot, ''))
Reference: https://docs.pytest.org/en/6.2.x/example/simple.html#post-process-test-reports-failures
Use pytest_runtest_makereport without conftest.py
It's possible, but you probably shouldn't do it.
# #pytest.mark.hookwrapper # Replace this
#pytest.hookimpl(hookwrapper=True) # with this
def pytest_runtest_makereport(item, call):
# ...
import gc
import sys
from pluggy.hooks import HookImpl
from _pytest.config import Config
config = next(o for o in gc.get_objects() if isinstance(o, Config))
hookimpl = HookImpl(sys.modules[__name__], __file__, pytest_runtest_makereport, pytest_runtest_makereport.pytest_impl)
config.hook.pytest_runtest_makereport._add_hookimpl(hookimpl)

pytest html - pass images to the hook in conftest.py from a test file

I have a test that produce a HTML output via pytest-html.
I get the report, but I would like to add a reference to the failure and to the expected image; I save those in my main test.py file, and I added the hook to conftest.py.
Now, I have no idea how to pass those images to the function; the hook is called after the test is performed; and currently I am hardcoding the output files and they are attached; but I would like to pass the path to the image from the test instead, especially because I need to write more tests that may be saved somewhere else from my usual folder, and may have different names.
This is the hook I have in conftest.py
#pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
timestamp = datetime.now().strftime('%H-%M-%S')
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
extra = getattr(report, 'extra', [])
if report.when == 'call':
# Attach failure image, hardcoded...how do I pass this from the test?
extra.append(pytest_html.extras.image('/tmp/image1.png'))
# test report html
extra.append(pytest_html.extras.url('http://www.theoutput.com/'))
xfail = hasattr(report, 'wasxfail')
if (report.skipped and xfail) or (report.failed and not xfail):
# only add additional data on failure
# Same as above, hardcoded but I want to pass the reference image from the test
extra.append(pytest_html.extras.image('/tmp/image2.png'))
extra.append(pytest_html.extras.html('<div>Additional HTML</div>'))
report.extra = extra
How can I pass to the hook, a variable containing the path of the image(s) to attach, from my pytest test files?
I found a workaround, although it is not pretty.
Adding a variable at module level in my test file, allow me to use item.module.varname, so if I set varname in my module test, and then assign it in the test; I can access it in pytest_runtest_makereport
in testfile.py
import pytest
myvar1 = None
myvar2 = None
class VariousTests(unittest.TestCase):
def test_attachimages():
global myvar1
global myvar2
myvar1 = "/tmp/img1.png"
myvar2 = "/tmp/img2.png"
in conftest.py
#pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
timestamp = datetime.now().strftime('%H-%M-%S')
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
extra = getattr(report, 'extra', [])
if report.when == 'call':
# Attach failure image
img1 = item.module.myvar1
img2 = item.module.myvar2
extra.append(pytest_html.extras.png(img1))
# test report html
extra.append(pytest_html.extras.url('http://www.theoutput.com/'))
xfail = hasattr(report, 'wasxfail')
if (report.skipped and xfail) or (report.failed and not xfail):
# only add additional data on failure
# Same as above, hardcoded but I want to pass the reference image from the test
extra.append(pytest_html.extras.png(img2))
extra.append(pytest_html.extras.html('<div>Additional HTML</div>'))
report.extra = extra

Pytest-html Customization

I am using pytest-html plugin for report generation for my test cases. I want to add a line item if the script fails. So here's my code---
import pytest
#pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
extra = getattr(report, 'extra', [])
if report.when == 'call':
# always add url to report
extra.append(pytest_html.extras.url('http://www.example.com/'))
xfail = hasattr(report, 'wasxfail')
if (report.skipped and xfail) or (report.failed and not xfail):
# only add additional html on failure
extra.append(pytest_html.extras.html('<div>Additional HTML</div>'))
report.extra = extra
def test_sayHello():
assert False, "I mean for this to fail"
print "hello"
def test_sayBye():
print "Bye"
I am running the scipt using -
pytest --html=report.html
I can see the report getting generatesd but it doesnt have a line item as Additional HTML.
Also is there a way via which I can add such line items in between of my scripts to the report.
Really appreciate help.
This should work:
#pytest.mark.hookwrapper
def pytest_runtest_makereport(item, call):
pytest_html = item.config.pluginmanager.getplugin('html')
outcome = yield
report = outcome.get_result()
extra = getattr(report, 'extra', [])
if report.when == 'call':
extra.append(pytest_html.extras.html('<p>some html</p>'))
report.extra = extra

Python Mock: Result of method call is __getitem__ instead of actual value

I have a unit test and mocking an external call. Here's the abbreviated code for the function I'm trying to test in service.py file
def post_data()
req = request.Request()
response = req.post(payload, url, json.dumps({"data": kwargs['data']}))
if response['request']['status'] == 'SUCCESS' and response['data']:
run_id = response.json()['data']['run_id']
response = track_run_to_completion(run_id, **kwargs)
return response
Here's my unit test method
#patch('service.request.Request.post')
def test_post_data(self, mock_post):
kwargs = {'a':'abc'}
expected = json.dumps({'request':{'status':'ERROR'},'data':{}})
mock_post.return_value = MagicMock(status_code=200, response=expected)
mock_post.assert_called_once_with({'action': 'trigger'}, 'a/abc', '{"data": {}}') # SUCCESS!
result = service.post_data(**kwargs)
print result
When I print the result, I was expecting to see the json, but get <MagicMock name='post()' id='4488707600'>. What am I missing here? I'm new to Python and started writing unit tests for an existing application.

Categories

Resources