Say I have this test in tests.py
def test_user(username='defaultuser'):
Case 1
I want to pass the username to test from the command line, something like
$ pytest tests.py::test_user user1 # could be --username=user1
How do I do that?
Case 2
I want to pass a list of usernames to test, like
$ pytest tests.py::test_user "user1, user2, user3"
I want to achieve something like
#pytest.mark.parametrize("username", tokenize_and_validate(external_param))
def test_user(username):
pass
def tokenize_and_validate(val):
if not val:
return 'defaultuser'
return val.split(',')
How can I do that?
Thank you
When you pass a parameter from the command line at first you need to create a generator method to get the value from the command line this method run every test.
def pytest_generate_tests(metafunc):
# This is called for every test. Only get/set command line arguments
# if the argument is specified in the list of test "fixturenames".
option_value = metafunc.config.option.name
if 'name' in metafunc.fixturenames and option_value is not None:
metafunc.parametrize("name", [option_value])
Then you can run from the command line with a command line argument:
pytest -s tests/my_test_module.py --name abc
Follow the link for more details
To mock the data, you can use fixtures or use builtin unittest mocks.
from unittest import mock
#mock.patch(func_to_mock, side_effect=func_to_replace)
def test_sth(*args):
pass
Command line options are also available.
Related
So I m loading test data from a different file based on the environment I'm meant to run the tests:
TestData/DevTestData.py contains:
data = {"accessToken": "Random Access Token"}
Then I have set up in conftest.py file:
To get the CLI parameter:
def pytest_addoption(parser):
parser.addoption('--environment', action='store')
Then to load the data I use LazySettings from simple-settings as a fixture:
#pytest.fixture
def testData(request):
return LazySettings("TestData." + request.config.getoption("--environment") + "TestData")
The test class looks like this:
class Test_User_Current():
userCurrentFacadeInstance = userCurrentGetAPI_Facade.User_Current_API_Facade()
def test_succesfull_request(self, environmentConfigs, testData):
self.userCurrentFacadeInstance.getRequest_user_current_API(environmentConfigs, testData).\
validateSuccessfullStatusCode().\
validateJsonContents()
CLI is:
py.test --environment Dev
My problem is, I have to pass "testData" for every test method rather then passing it to User_Current_API_Facade()'s constructor, and I cant do that for some reason, if I'm passing it to the constructor and not the test method (test_succesfull_request()) it does not work.
Do you guys have any idea on how to do this in a better way?
Imagine a toy test definition:
#pytest.mark.parametrize("bar", [1, 2, 3])
def test_foo(bar):
assert some_result(bar)
I now want to be able to provide bar on the command line. As far as I know this is not built into Py.Test yet:
pytest --param "bar=7"
And write my test parameter definition like this:
#pytest.mark.parametrize(*from_args_if_available("bar", [1, 2, 3]))
def test_foo(bar):
assert some_result(bar)
I can implement from_args_if_available() parsing all command line arguments again and looking for --params of course. Another way would be to make request.config available globally (which would be ugly).
I wonder if it's possible to access the request.config element from anywhere in order to have all command line option definition at one place (and thus the help).
So is there another way to access the parsed command line options which I can use in the described way?
I'll post my currently working solution here:
in a file utils.py I define a function param:
def param(name, default_value):
parser = argparse.ArgumentParser()
parser.add_argument("--param", action="store")
args, unknown = parser.parse_known_args()
try:
if args.param:
for p_name, p_value in (param.split("=") for param in args.param.split(";")):
if p_name == name:
return p_name, ast.literal_eval(p_value)
except (SyntaxError, ValueError):
raise RuntimeError(
"Wrong parameter definition: %r, must be 'name1=<expr>;name2=<expr>;..'"
% args.param)
return name, default_value
Which can be used like this:
import pytest
from util import param
#pytest.mark.parametrize(*param("bar", [1, 2, 3]))
def test_foo(bar, env_type):
print("param: ", "bar", bar)
assert bar < 7
A pytest call can look like this now:
pytest --param "bar=[5,6,7];unused=range(5)"
in conftest.py I now have to add the param argument as well - otherwise an exception gets raised when I define parameters on command line:
def pytest_addoption(parser):
parser.addoption("--param", action="store", help="overwrite test parameters")
Upside: it works :)
Downside: It's an ugly workaround, param() can't be defined inside conftest.py
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.
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'])
I have a function that I don't want to run every time I run tests in my Flask-RESTFul API. This is an example of the setup:
class function(Resource):
def post(self):
print 'test'
do_action()
return {'success':True}
in my test I want to run this function, but ignore do_action(). How would I make this happen using pytest?
This seems like a good opportunity to mark the tests
#pytest.mark.foo_test
class function(Resource):
def post(self):
print 'test'
do_action()
return {'success':True}
Then if you call with
py.test -v -m foo_test
It will run only those tests marked "foo_test"
If you call with
py.test -v -m "not foo_test"
It will run all tests not marked "foo_test"
You can mock do_action in your test:
def test_post(resource, mocker):
m = mocker.patch.object(module_with_do_action, 'do_action')
resource.post()
assert m.call_count == 1
So the actual function will not be called in this test, with the added benefit
that you can check if the post implementation is actuall calling the function.
This requires pytest-mocker to
be installed (shameless plug).