pytest.yield_fixture using cmd line options - python

is it possible to use pytest_addoption(parser) to create a list that is used by pytest.yield_fixture? i.e.
def pytest_addoption(parser):
parser.addoption("-foo", action="store",defaults="1,2,3")
#pytest.yield_fixture(params=request.config.getoption('--foo').split(','))
def test_bar(request):
do_something(request.param)
say you had 6 browsers, and you wanted to ability to run the tests against 1 browser as a quick check. I can't figure out how to get in place before test discovery/generation. Help

This obviously doesn't work since you the request variable does not exist in the global module scope, which is when the expression in the decorator is executed. The way to solve this is use the pytest_generate_tests hook:
# conftest.py
def pytest_addoption(parser):
parser.addoption('--foo', action='store', defaults='1,2,3')
def pytest_configure(config):
config.foo = config.getoption('foo').split(',')
def pytest_generate_tests(metafunc):
if 'foo' in metafunc.fixturenames:
metafunc.parametrize('foo', metafunc.config.foo)
# test_bar.py
def test_bar(foo):
do_something(foo)

Related

All my test functions are loading a fixture that is in the conftest.py, even when they don't need it

I have 2 different test files and some fixtures in my conftest.py:
1)"Test_dummy.py" which contains this function:
def test_nothing():
return 1
2)"Test_file.py". which contains this function:
def test_run(excelvalidation_io):
dfInput, expectedOutput=excelvalidation_io
output=run(dfInput)
for key, df in expectedOutput.items():
expected=df.fillna(0)
real=output[key].fillna(0)
assert expected.equals(real)
3)"conftest.py" which contains these fixtures:
def pytest_generate_tests(metafunc):
inputfiles=glob.glob(DATADIR+"**_input.csv", recursive=False)
iofiles=[(ifile, getoutput(ifile)) for ifile in
inputfiles]
metafunc.parametrize("csvio", iofiles)
#pytest.fixture
def excelvalidation_io(csvio):
dfInput, expectedOutput= csvio
return(dfInput, expectedOutput)
#pytest.fixture
def client():
client = app.test_client()
return client
When i run the tests, "Test_dummy.py" also tries to load the "excelvalidation_io" fixture and it generates error:
In test_nothing: function uses no argument 'csvio'
I have tried to place just the fixture inside the "Test_file.py" and the problem is solved, but i read that it's a good practice to locate all the fixtures in the conftest file.
The function pytest_generate_tests is a special function that is always called before executing any test, so in this case you need to check if the metafunc accepts an argument named "csvio" and do nothing otherwise, as in:
def pytest_generate_tests(metafunc):
if "excelvalidation_io" in metafunc.fixturenames:
inputfiles=glob.glob(DATADIR+"**_input.csv", recursive=False)
iofiles=[(ifile, getoutput(ifile)) for ifile in
inputfiles]
metafunc.parametrize("csvio", iofiles)
Source

How can I use pytest options as fixture and not repeat myself?

I have a test suite with a conftest.py defining some options and some fixtures to retrieve them:
def pytest_addoption(parser):
parser.addoption("--ip", action="store")
parser.addoption("--port", action="store")
#pytest.fixture
def ip(request):
return request.config.getoption("ip")
#pytest.fixture
def port(request):
return request.config.getoption("ip")
(I slipped in a copy-paste error to make a point)
My tests can very eloquently express the options they need:
def test_can_ping(ip):
...
def test_can_net_cat(ip, port):
...
But ...
I'm trying to avoid duplicating myself here: I have to specify the name of the config parameter in three places to make it work.
I could have avoided the copy-paste error if I had something that looked like this:
# does not exist:
#pytest.option_fixture
def ip(request, parser):
return request.config.getoption(this_function_name)
or this
def pytest_addoption(parser):
# does not exist: an as_fixture parameter
parser.addoption("--ip", action="store", as_fixture=True)
parser.addoption("--port", action="store", as_fixture=True)
Is there a way to tell pytest to add an option and a corresponding
fixture to achieve DRY/SPOT code?
After some tests, I came to something working. It is probably not the best way to do it but it is quite satisfying I think.
All code below have been added to the conftest.py module, except the two tests.
First define a dictionary containing the options data:
options = {
'port': {'action': 'store', 'help': 'TCP port', 'type': int},
'ip': {'action': 'store', 'help': 'IP address', 'type': str},
}
We could do without help and type, but it will have a certain utility later.
Then you can use this options to create the pytest options:
def pytest_addoption(parser):
for option, config in options.items():
parser.addoption(f'--{option}', **config)
At this point, pytest --help gives this (note the help data usage which provides convenient doc):
usage: pytest [options] [file_or_dir] [file_or_dir] [...]
...
custom options:
--port=PORT TCP port
--ip=IP IP address
Finally we have to define the fixtures. I did this by providing a make_fixture function which is used in a loop at conftest.py reading to dynamically create fixtures and add them to the global scope of the module:
def make_fixture(option, config):
func = lambda request: request.config.getoption(option)
func.__doc__ = config['help']
globals()[option] = pytest.fixture()(func)
for option, config in options.items():
make_fixture(option, config)
Again, the 'help' data is used to build a docstring to the created fixtures and document them. Thus, invoking pytest --fixtures prints this:
...
---- fixtures defined from conftest ----
ip
IP address
port
TCP port
Invoking pytest --port 80 --ip 127.0.0.1, with the two following very simple tests, seems to validate the trick (Here the type data shows its utility, it has made pytest convert the port to an int, instead of a string):
def test_ip(ip):
assert ip == '127.0.0.1'
def test_ip_port(ip, port):
assert ip == '127.0.0.1'
assert port == 80
(Very interesting question, I would like to see more like this one)
Instead of changing the pytest decorators, create one of your own:
parse_options = []
#addOption(parse_options)
#pytest
def ip(...): ...
A decorator doesn't have to modify the function which is passed in. So in this case, look at the method object, use the f.__name__ to get the name and add an entry in the list parse_options for it.
The next step is to modify pytest_addoption to iterate over the list and create the options. At the time when the function is being executed, the decorators should have done their work.

Pytest: How to access command line arguments outside of a test

I would like to access command line arguments passed to pytest from within a non-test class.
I have added the following to my conftest.py file
def pytest_addoption(parser): # pragma: no cover
"""Pytest hook to add custom command line option(s)."""
group = parser.getgroup("Test", "My test option")
group.addoption(
"--stack",
help="stack", metavar="stack", dest='stack', default=None)
But I can not work out how to access the value passed on the command line. I have found code on how to access it from a fixture, but I would like to access it from a method or class that is not part of the test case.
You can access the parameters with sys.argv.
It will return a list of all the arguemnts you sent wrote when you called using the command line.
For example
def pytest_addoption(parser): # pragma: no cover
"""Pytest hook to add custom command line option(s)."""
params = sys.argv[1:]
group = parser.getgroup("Test", "My test option")
group.addoption(
"--stack",
help="stack", metavar="stack", dest='stack', default=None)
Yes you could add one more pytest hook pytest_configure to your conftest.py as follows:
stack = None
def pytest_configure(config):
global stack
stack = config.getoption('--stack')
Now your argument stack is available at global level.

How to pass arguments to pytest if pytest is run programmatically from another module?

In the following example, how do I pass args of run_tests() to pytest.main(...) so that I can use args for the test methods of TestFooBar in test_module.py?
my_module.py
def run_tests(args):
# How do I pass parameter 'args' to pytest here.
pytest.main(['-q', '-s', 'test_module.py::TestFooBar'])
test_module.py
class TestFooBar():
# How do I get 'args' of 'run_tests()' from 'my_module.py' here.
#pytest.mark.parametrize("args", [args])
def test_something(args):
assert 'foo' == args['foo']
#pytest.mark.parametrize("args", [args])
def test_something_else(args):
assert 'bar' == args['bar']
If you execute pytest.main the you are doing the equivalent to calling py.test from the command line, so the only way to pass arguments that I am aware of is via a command line parameter. For this to be possible, your parameters need to be convertable to and from string.
Basically this means creating a conftest.py with the following
def pytest_addoption(parser):
parser.addoption('--additional_arguments', action='store', help='some helptext')
def pytest_configure(config):
args = config.getoption('additional_arguments')
Now do something with args: deserialize it, make it a global variable, make it a fixture, anything you want. Fixtures from conftest.py will be available to the entire test.
Needless to say that your call should now include the new parameter:
pytest.main(['-q', '-s', '--additional_arguments', args_string, 'test_module.py::TestFooBar'])

How can I use a pytest fixture as a parametrize argument?

I have a pytest test like so:
email_two = generate_email_two()
#pytest.mark.parametrize('email', ['email_one#example.com', email_two])
def test_email_thing(self, email):
... # Run some test with the email parameter
Now, as part of refactoring, I have moved the line:
email_two = generate_email_two()
into its own fixture (in a conftest.py file), as it is used in various other places. However, is there some way, other than importing the fixture function directly, of referencing it in this test? I know that funcargs are normally the way of calling a fixture, but in this context, I am not inside a test function when I am wanting to call the fixture.
I ended up doing this by having a for loop in the test function, looping over each of the emails. Not the best way, in terms of test output, but it does the job.
import pytest
import fireblog.login as login
pytestmark = pytest.mark.usefixtures("test_with_one_theme")
class Test_groupfinder:
def test_success(self, persona_test_admin_login, pyramid_config):
emails = ['id5489746#mockmyid.com', persona_test_admin_login['email']]
for email in emails:
res = login.groupfinder(email)
assert res == ['g:admin']
def test_failure(self, pyramid_config):
fake_email = 'some_fake_address#example.com'
res = login.groupfinder(fake_email)
assert res == ['g:commenter']

Categories

Resources