pytest doesn't recognize parametrs send from command line - python

While my practice I try to build my hybrid test framework. Here is link to GitHub repository of My framework
Its structure is
...root/
...Base/
...Locators/
...Pages/
...Tests/
...conftest.py
...pytest.ini
I would like to make possibility to pass configurations from terminal command or from conftest.py file
But each time I run command:
pytest search_test.py --browser chrome --server server_ip/wd/hub
I get error:
ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...]
pytest: error: unrecognized arguments: --browser chrome --server server_ip/wd/hub/wd/hub
inifile: path to inifile
rootdir: path to root directory
here is content of my conftest.py file:
import pytest
import configparser
from selenium import webdriver
def environment_options(parser):
parser.addoption('--browser', '-B', dest='BROWSER', choises=['chrome', 'chr', 'firefox', 'ff'],
help=f"possible values are: {['chrome', 'chr', 'firefox', 'ff']}")
parser.addoption('--server', '-S', dest="SERVER")
#pytest.fixture(scope='class')
def environment_configuration(request):
read_config = configparser.ConfigParser()
# checking if browser was input from console or from config file from section Environment and assigment
# it to browser_name variable
browser_name = request.config.getoption(
"BROWSER") or read_config.get("Environments", "browser")
# checking if remote server was input from console or from config file from section Environment and assigment
# it to remote_server variable
remote_server = request.config.getoption(
"SERVER") or read_config.get("Environments", "remote_server")
try:
request.cls.driver = webdriver.Remote(
command_executor=remote_server,
desired_capabilities={
"browserName": browser_name})
except BaseException:
print("check browser or remote server configs")
yield request.cls.driver
request.cls.driver.close()
request.cls.driver.quit()

need to change name of function from environment_options to pytest_addoption and everything starts to work :-)

Related

Getting fixture 'setup' not found error while running pytest

I am getting following error while running pytest using following code im unable to figure out whats wrong please find below code snippets.
Console ouput :
PS C:\Bhargav\Python Learning\Projects\MBDailyBoost> pytest
================================================================================= test session starts =================================================================================
platform win32 -- Python 3.10.2, pytest-7.1.2, pluggy-1.0.0
rootdir: C:\Bhargav\Python Learning\Projects\MBDailyBoost
plugins: allure-pytest-2.9.45, html-3.1.1, metadata-2.0.2
collected 2 items
E fixture 'setup' not found
> available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, extra, include_metadata_in_junit_xml, metadata, monkeypatch, pytestconfig, recor
d_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
> use 'pytest --fixtures [testpath]' for help on them.
C:\Bhargav\Python Learning\Projects\MBDailyBoost\testCases\test_login.py:22
=============================================================================== short test summary info ===============================================================================
ERROR testCases/test_login.py::Test_001_Login::test_homePageTitle
ERROR testCases/test_login.py::Test_001_Login::test_Login
My conftest.py file contains below code:
import pytest
from selenium import webdriver
#pytest.fixture(scope="class")
def setup():
global driver
# driver = webdriver.Chrome(executable_path="C:\\chromedriver.exe")
driver = webdriver.Chrome()
driver.maximize_window()
return driver
My test_login.py file contains below code:
import time
from pageObjects.LoginPageGMB import LoginPage
class Test_001_Login:
baseURL = ""
usename = ""
password = ""
def test_homePageTitle(self, setup):
self.driver = setup
self.driver.get(self.baseURL)
self.driver.maximize_window()
actual_title = self.driver.title
if actual_title == "Google Business Profile – Get Listed on Google":
assert True
else:
assert False
Keep both code snippets in the same module
declare all your fixtures inside conftest.py under test folder.

How to generate pytest-html report in a dynamically created log directory [duplicate]

I have a problem with setting report name and folder with it dynamically in Python's pytest.
For example: I've run all pytest's tests # 2020-03-06 21:50 so I'd like to have my report stored in folder 20200306 with name report_2150.html. I want it to be automated and triggered right after the tests are finished.
I'm working in VS Code and I'm aiming to share my work with colleagues with no automation experience so I'm aiming to use it as "click test to start".
My project structure:
webtools/
|── .vscode/
|──── settings.json
|── drivers/
|── pages/
|── reports/
|── tests/
|──── __init__.py
|──── config.json
|──── conftest.py
|──── test_1.py
|──── test_2.py
|── setup.py
Code samples:
settings.json
{
"python.linting.pylintEnabled": false,
"python.linting.flake8Enabled": true,
"python.linting.enabled": true,
"python.pythonPath": "C:\\Users\\user\\envs\\webtools\\Scripts\\python.exe",
"python.testing.pytestArgs": [
"tests",
"--self-contained-html",
"--html=./reports/tmp_report.html"
],
"python.testing.unittestEnabled": false,
"python.testing.nosetestsEnabled": false,
"python.testing.pytestEnabled": true,
"python.testing.unittestArgs": [
"-v",
"-s",
"./tests",
"-p",
"test_*.py"
]
}
config.json
{
"browser": "chrome",
"wait_time": 10
}
conftest.py
import json
import pytest
from datetime import datetime
import time
import shutil
import os
from selenium import webdriver
from selenium.webdriver import Chrome
CONFIG_PATH = 'tests/config.json'
DEFAULT_WAIT_TIME = 10
SUPPORTED_BROWSERS = ['chrome', 'explorer']
#pytest.fixture(scope='session')
def config():
# Read the JSON config file and returns it as a parsed dict
with open(CONFIG_PATH) as config_file:
data = json.load(config_file)
return data
#pytest.fixture(scope='session')
def config_browser(config):
# Validate and return the browser choice from the config data
if 'browser' not in config:
raise Exception('The config file does not contain "browser"')
elif config['browser'] not in SUPPORTED_BROWSERS:
raise Exception(f'"{config["browser"]}" is not a supported browser')
return config['browser']
#pytest.fixture(scope='session')
def config_wait_time(config):
# Validate and return the wait time from the config data
return config['wait_time'] if 'wait_time' in config else DEFAULT_WAIT_TIME
#pytest.fixture
def browser(config_browser, config_wait_time):
# Initialize WebDriver
if config_browser == 'chrome':
driver = webdriver.Chrome(r"./drivers/chromedriver.exe")
elif config_browser == 'explorer':
driver = webdriver.Ie(r"./drivers/IEDriverServer.exe")
else:
raise Exception(f'"{config_browser}" is not a supported browser')
# Wait implicitly for elements to be ready before attempting interactions
driver.implicitly_wait(config_wait_time)
# Maximize window for test
driver.maximize_window()
# Return the driver object at the end of setup
yield driver
# For cleanup, quit the driver
driver.quit()
#pytest.fixture(scope='session')
def cleanup_report():
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
os.chdir("./reports")
os.mkdir(timestamp)
yield
shutil.move("./tmp_report.html", "./%s/test_report.html" % timestamp)
In current situation the report is created as tmp_report.html in the reports folder, but I don't know how I can force running cleanup_report() after all tests are completed and tmp_report.html is present and complete in folder. For checking if complete I assume I'd have to verify if all html tags have their closing (or at least <html> one).
Can somebody help me with that? If you need some further code portions I'll provide them as soon as possible.
Thank you in advance!
You can customize the plugin options in a custom impl of the pytest_configure hook. Put this example code in a conftest.py file in your project root dir:
from datetime import datetime
from pathlib import Path
import pytest
#pytest.hookimpl(tryfirst=True)
def pytest_configure(config):
# set custom options only if none are provided from command line
if not config.option.htmlpath:
now = datetime.now()
# create report target dir
reports_dir = Path('reports', now.strftime('%Y%m%d'))
reports_dir.mkdir(parents=True, exist_ok=True)
# custom report file
report = reports_dir / f"report_{now.strftime('%H%M')}.html"
# adjust plugin options
config.option.htmlpath = report
config.option.self_contained_html = True
If you want to completely ignore what's passed from command line, remove the if not config.option.htmlpath: condition.
If you want to stick with your current impl, notice that on fixtures teardown, pytest-html hasn't written the report yet. Move the code from cleanup_report to a custom impl of the pytest_sessionfinish hook to ensure pytest-html has already written the default report file:
#pytest.hookimpl(trylast=True)
def pytest_sessionfinish(session, exitstatus):
shutil.move(...)

How to set dynamic default parameters for py.test?

I have a framework which working under py.test. py.test can generate beauty reports with params --html and --junitxml. But clients that using my framework not always type this params to command line where they using py.test. I want make py.test to generate reports always when the py.test used with my framework. And i want to put this reports with log folder. So i need to generate path for report in runtime. Can i do this by fixtures? Or maybe by the plugin API?
Putting this in conftest.py will suffice:
def pytest_configure(config):
if config.option.xmlpath is None:
config.option.xmlpath = get_custom_xml_path() # implement this
The accepted answer is probably a bit more complicated than necessary for most people for a few reasons:
The decorator doesn't help. It doesn't matter when this executes.
There is no need make a custom LogXML since you can just set the property here and it will be used.
slaveinput is specific to a pytest plugin xdist. I don't think there is any need to check for that, especially if you don't use xdist.
First of all, if you want to implicitly add the command line args to pytest, you can use the pytest.ini placed in the tests root dir with the addopts config value:
[pytest]
addopts=--verbose --junit-xml=/tmp/myreport.xml # etc
Of course, if you want to dynamically calculate the directory to store the reports, then you can't put it in the config and will need to extend pytest. The best spot would be the pytest_configure hook. Example:
# conftest.py
import tempfile
import pytest
from _pytest.junitxml import LogXML
#pytest.hookimpl(tryfirst=True)
def pytest_configure(config):
if config.option.xmlpath: # was passed via config or command line
return # let pytest handle it
if not hasattr(config, 'slaveinput'):
with tempfile.NamedTemporaryFile(suffix='.xml') as tmpfile:
xmlpath = tmpfile.name
config._xml = LogXML(xmlpath, config.option.junitprefix, config.getini('junit_suite_name'))
config.pluginmanager.register(config._xml)
If you remove the first if block, then pytest will completely ignore --junit-xml arg passed via command line or in addopts value in config.
Example run:
$ pytest
=================================== test session starts ====================================
platform darwin -- Python 3.6.3, pytest-3.3.1, py-1.5.2, pluggy-0.6.0
rootdir: /Users/hoefling/projects/private/stackoverflow/so-48320357, inifile:
plugins: forked-0.2, asyncio-0.8.0, xdist-1.22.0, mock-1.6.3, hypothesis-3.44.4
collected 1 item
test_spam.py . [100%]
--- generated xml file: /var/folders/_y/2qk6029j4c7bwv0ddk3p96r00000gn/T/tmp1tleknm3.xml ---
================================ 1 passed in 0.01 seconds ==================================
The xml report is now put in a tempfile.
Configure pytest.ini file with parameters:
# content of pytest.ini
[pytest]
addopts = --html=report.html --self-contained-html
;addopts = -vv -rw --html=./results/report.html --self-contained-html
#hoefling's answer worked perfectly for me in conftest.py. the code looks simpler there.
def pytest_configure(config):
if not config.option.xmlpath and not hasattr(config, 'slaveinput'):
xmlpath = "test_report_" + str(int(time.time())) + ".xml"
config._xml = LogXML(xmlpath, config.option.junitprefix, config.getini('junit_suite_name'))
config.pluginmanager.register(config._xml)
Just to keep things more clear, pytest uses argparse and the request.config.option is a argparse.Namespace object. Then, if you would like to simulate a command line option as pytest ... --docker-compose-remove-volumes, you can directly attribute the option docker_compose_remove_volumes to request.config.option (because --docker-compose-remove-volumes is converted to docker_compose_remove_volumes by argparse module).
This examples inverts the default option for --docker-compose-remove-volumes which is false. But allow you to enable it back by providing --keep-containers option to pytest.
def pytest_addoption(parser):
parser.addoption("--keep-containers", action="store_true", default=False,
help="Keeps docker-compose on failure.")
#pytest.fixture(scope='session', autouse=True)
def load_env(request):
is_to_keep_container = request.config.getoption("--keep-containers")
if not is_to_keep_container:
request.config.option.docker_compose_remove_volumes = True

pytest command line argument failing

I have added conftest.py at the same directory level as my test file . The content of my conftest.py :
import pytest
def pytest_addoption(parser):
parser.addoption("--l", action="store", help="Get license value")
#pytest.fixture
def lic(request):
return request.config.getoption("--l")
and following is my test file def
def test(lic):
print "testing license : %s"%lic
assert 0
But i still get following error:
pytest .\source\test_latestLinuxAgent.py --l=test
pytest : usage: pytest [options] [file_or_dir] [file_or_dir] [...]
At line:1 char:1
+ pytest .\source\test_latestLinuxAgent.py --l=test
pytest: error: ambiguous option: --l=test could match --lf, --last-failed
As the response says, --l option is ambiguous. What does it mean?
Let me explain it with an example. If you have --zaaaa option, the shortcut for it is --z. However, there is one condition: --zaaaa must be the only option starting with z char. Otherwise, the interpreter does not know which option should be chosen.
You can't define --l option because there are two options starting with l char: --lf and --last-failed.
I suggest creating non-conflicting option, --license would be nice.

Python Selenium Exception AttributeError: "'Service' object has no attribute 'process'" in selenium.webdriver.ie.service.Service

I have a Selenium Python test suite. It starts to run but after a few mins the following error is thrown:
Exception AttributeError: "'Service' object has no attribute 'process'" in <bound method Service.__del__ of <selenium.webdriver.ie.service.Service object at 0x0000000002610DD8>> ignored
My test suite implementation is:
import unittest
from HTMLTestRunner2 import HTMLTestRunner
import os
import Regression_TestCase.RegressionProject_TestCase2
# get the directory path to output report file
#result_dir = os.getcwd()
result_dir = r"E:\test_runners\selenium_regression_test_5_1_1\ClearCore - Regression Test\TestReport"
# get all tests from SearchProductTest and HomePageTest class
search_tests = unittest.TestLoader().loadTestsFromTestCase(Regression_TestCase.RegressionProject_TestCase2.RegressionProject_TestCase2)
# create a test suite combining search_test
re_tests = unittest.TestSuite([search_tests])
# open the report file
outfile = open(result_dir + "\TestReport.html", "w")
# configure HTMLTestRunner options
runner = HTMLTestRunner.HTMLTestRunner(stream=outfile,
title='Test Report',
description='Smoke Tests')
# run the suite using HTMLTestRunner
runner.run(re_tests)
Can anyone help why this error is stopping my test suite from running? How do I solve this?
Provided you have installed selenium, and assuming that earlier in the console's traceback log you also got something like "'chromedriver' executable needs to be in PATH" in your script, you should be able to do:
from selenium import webdriver
driver = webdriver.Chrome("/path/to/chromedriver")
This should tell your script where to find chromedriver. On a Mac you can usually use: /usr/local/bin/chromedriver
Download chromium driver from https://sites.google.com/a/chromium.org/chromedriver/downloads
Unzip the file and then from your code, write something like:
from selenium import webdriver
driver = webdriver.Chrome("/path/to/chromedriver")
where /path/to/chromedriver is the location of your chromedriver.
This is the class declaration for Chrome Webdriver: selenium.webdriver.chrome.webdriver.WebDriver(executable_path='chromedriver', ...
taken from https://seleniumhq.github.io/selenium/docs/api/py/webdriver_chrome/selenium.webdriver.chrome.webdriver.html#module-selenium.webdriver.chrome.webdriver
Given what #CubeBot88 had already written, another way to get the chromedriver executable in PATH is to do as follow:
from os
from selenium import webdriver
os.environ['PATH'] += "/path/to/chromedriver"
driver = webdriver.Chrome()
The above way puts the path to chromedriver to environment variable PATH only in this program, allowing independent PATH in different situations.
This page comes up first on Google, but only suggests that you are declaring you path incorrectly.
If you are reading this, try making sure the chromedriver is an executable:
run this command in the path of the driver:
sudo chmod a+x chromedriver

Categories