Tuesday, February 21, 2017

PyTest with Selenium - HTML report with Screenshot

recently I was working with Py.Test with selenium. It's really powerful tool with awesome fixtures. It generates JUnit XML report as well which is good to be consumed by Jenkins. However, I needed an HTML report as well with embedded screenshot for the ease of identifying the issue. And this is how I did it finally after doing some googling.

Generate HTML Report of PyTest 

Pytest-html plugin is available which you can use to generate the beautiful Report. The all you have to do is install the plugin and run your test case with this plugin. 

Embede ScreenShot When Test Case Fails

PyTest-html plugin allows you to extend the plugin and that's the best thing about this plugin. 
conftest.py

from selenium import webdriver
import pytest
driver = None


@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)


@pytest.fixture(scope='session', autouse=True)
def browser():
    global driver
    if driver is None:
        driver = webdriver.Firefox()
    return driver
test_screenshot.py
def test_screenshot_on_test_failure(browser):
    # driver = webdriver.Firefox()
    browser.get("https://google.com")
    assert False

6 comments:

mubarak smiley said...

If would be good if you can explain in detail by giving 2 or 3 examples.

Gaurang shah said...

@mubarak I have already given one example, it's matter of duplicating this tests.

Anonymous said...

Hi Gaurang, Thanks for the code.I tried implementing the same but its throwing me errors .
INTERNALERROR> return _wrapped_call(hook_impl.function(*args), self.execute)
INTERNALERROR> File "C:\Users\Administrator\Venv_Hoteltap\lib\site-packages\_pytest\vendored_packages\pluggy.py", line 250, in _wrapped_call
INTERNALERROR> wrap_controller.send(call_outcome)
INTERNALERROR> File "D:\Hoteltap\src\main\scripts\conftest.py", line 21, in pytest_runtest_makereport
INTERNALERROR> _capture_screenshot(file_name)
INTERNALERROR> File "D:\Hoteltap\src\main\scripts\conftest.py", line 31, in _capture_screenshot
INTERNALERROR> driver.get_screenshot_as_file(name)
INTERNALERROR> AttributeError: 'NoneType' object has no attribute 'get_screenshot_as_file'

Can you please help me out

thanks
Swetha

Gaurang shah said...

@Swetha, it seems like driver object is null, if you using exact code in the way I have explained it should not give you any error. if you have just copied part of the code and using different way to initialize the driver object then you will have to debug your code to check why driver object is null.

Anonymous said...

Only difference is i am using chrome driver instead of firefox.Here is the code where i changed
@pytest.fixture(scope='session', autouse=True)
def browser():
global driver
if driver is None:
driver = webdriver.chrome("C:\Users\Administrator\Downloads\chromedriver.exe")
return driver

Gaurang shah said...

@Swetha, before returning driver could you check if it's null or not.

Post a Comment