How do I add cli arguments to py.test? - python

Im trying to have interchangeable config files and I'm using py.test to send the testing suite to the cloud.
The following works when I run them locally using python main.py --site site1 in the terminal.
I'm trying to figure out how I can add cli arguments so that it will work with py.test
I have 3 files. main, config, and site1_config
main.py
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='CLI tests for an environment')
parser.add_argument('--site', nargs='?', help='the site to use for the config')
parser.add_argument('unittest_args', nargs='*')
#Get our property
args = parser.parse_args()
if args.site:
config.localizeConfig(args.site)
sys.argv[1:] = args.unittest_args
unittest.main(verbosity = 2)
config.py
def localizeConfig(site):
localizedConfig = __import__(site+'_config');
# localizedConfig = __import__(site);
#print dir(localizedConfig)
props = filter(lambda a: not a.startswith('__'), dir(localizedConfig))
#Iterate over each property and set it on the config
for prop in props:
if prop != 'os' and prop != 'sys' and prop != 'webdriver':
globals()[prop] = getattr(localizedConfig, prop)
host_url = www.google.com
site1_config.py
host_url = www.yahoo.com
Im trying to set a flag so that when py.test -n6 --boxed main.py site1
is run, the site1_config.py will copy over its contents into config.py
I'm not sure how I can get this to work with py.test
usage: py.test [options] [file_or_dir] [file_or_dir] [...]
py.test: error: unrecognized arguments:

I don't really get what you are trying to do, but in order to add CLI arguments to py.test check:
http://pytest.org/latest/example/simple.html:
# content of conftest.py
import pytest
def pytest_addoption(parser):
parser.addoption("--cmdopt", action="store", default="type1",
help="my option: type1 or type2")

The equivalent of your unittest.main(verbosity = 2) is pytest.main(['-v'])
Things to note:
In pytest, I'm not sure verbosity level is selectable
Some of the references on pytest.main(input) don't specify that the input has to be a list, but as of today it must be.
https://docs.pytest.org/en/latest/usage.html

Related

How to add arguments to Python script entrypoint based on conditions

I have an entry point like so:
ENTRYPOINT [ "python" ,"/usr/local/bin/script.py" ]
I want to be able to add multiple arguments to script.py if a particular container has "MY_ENV_VAR" set to TRUE.
So for instance if:
MY_ENV_VAR = true
MY_ENV_VAR2 = true
MY_ENV_VAR3 = false (or anything other than true)
I want to run
/usr/local/bin/script.py --my-env-var --my-env-var2
I can't find a good working example of how to accomplish this
You will not be able to conditionally do this within the Dockerfile, but you can achieve the desired behavior by configuring the Python script script.py as follows:
import argparse
import os
def main():
parser = argparse.ArgumentParser("Arguments")
if os.environ.get("MY_ENV_VAR") is not None:
parser.add_argument("--my-env-var", required=True, action="store_true")
if os.environ.get("MY_ENV_VAR2") is not None:
parser.add_argument("--my-env-var2", required=True, action="store_true")
parser.parse_args()
if __name__ == "__main__":
main()
The logic is that if MY_ENV_VAR or MY_ENV_VAR2, then a required argparse argument is set.

How to read command line argument while running py.test -sv B/

I have a file conftest.py in directory A. I have some test files in directory B which is in A. I run all of them using py.test -sv B/. However sometime I want to pass argument -val="Hello" and store in a file.
I am trying to do it as following:
import argparse
parser=argparse.ArgumentParser()
parser.add_argument("val",help="value")
args=parser.parse_args()
print(args.val)
a_file=open("file_val","w")
a_file.write(args.val)
a.file.close()
def pytest_addoption(parser):
parser.addoption("--name", action="store", default="default name")
However it doesn't write anything and gives error: unrecognized arguments: -sv —val=hello, when I run py.test -sv B/ —val=Hello
so I tried following :
import argparse
def pytest_addoption(parser):
parser.addoption("--name", action="store", default="default name")
parser.add_argument("val",help="value")
args=parser.parse_args()
print(args.val)
a_file=open("file_val","w")
a_file.write(args.val)
a.file.close()
But it gives error : No method add_argument when I run py.test -sv B/ —val=Hello
read command line argument while running pytest -sv B/
It should work for you:
First of all you need to pass the argument using addoption and fixture that will read the option.
For that you should create a separate file and name it conftest.py in your test folder.
The content of the conftest.py file:
import pytest
def pytest_addoption(parser):
parser.addoption("--name", action="store", default="default name")
#pytest.fixture
def name(request):
return request.config.getoption("--name")
Then you need to create a test function with an argument which named after the fixture in the separate file, or like in your case, even in a separate folder.
Content of B/test_write_file.py:
def test_write_file(name):
print(name)
a_file = open("file_val.txt", "w")
a_file.write(name)
a_file.close()
assert 0 # or whatever you want
After that you can freely run your test from the test root folder:
pytest -sv B/ --name=Hello
Output:
________________ test_write_file _________________
name = 'Hello'
def test_write_file(name):
print(name)
a_file = open("file_val.txt", "w")
a_file.write(name)
a_file.close()
> assert 0
E assert 0
B\test_write_file.py:8: AssertionError
You don't need to use argparse, pytest does everything for you.
The structure of the test folder:
Just like in this example:
https://docs.pytest.org/en/6.2.x/example/simple.html
Cheers!

python click framework - custom multi command implementation in oop's method

I have written some scripts that I'm trying to integrate with click. All the scripts are written in python OOP's.
The issue is that i am trying to build command section in oop's way but couldn't do it properly.
let me show you, what i am trying to do and please note that i am sharing here dummy code it is very similar to the real code.
First thing the directory structure:
-customcmd <dir>
|
|->commands <dir>
| -> abc-command.py
| -> __init__.py
|
|->__init__.py
|->main.py
|->setup.py
1) I have created one file called main.py, which contains following code:
import click
import os
plugin_folder = os.path.join(os.path.dirname(__file__), 'commands')
class MyCLI(click.MultiCommand):
def list_commands(self, ctx):
rv = []
for filename in os.listdir(plugin_folder):
if filename.startswith('__'):
continue
if filename.endswith('.py'):
rv.append(filename[:-3])
rv.sort()
return rv
def get_command(self, ctx, name):
ns = {}
fn = os.path.join(plugin_folder, name + '.py')
with open(fn) as f:
code = compile(f.read(), fn, 'exec')
eval(code, ns, ns)
return ns['cli']
cli = MyCLI()#help='This tool\'s subcommands are loaded from a ''plugin folder dynamically.'
if __name__ == '__main__':
cli()
2) abc-command.py
#click.command()
#click.option("--file-loc", '-fl', type=open, required=True, default=None, help="Path to the file")
def cli(file_loc):
"""
This is test command
"""
print("Path to file {}".format(file_loc))
Output of above code when you call main.py:
(automation) K:\Pythonenv\automation\customcmd>python main.py
Usage: main.py [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
policyresult This is test command
Output of above code when you call sub command:
(automation) K:\Pythonenv\automation\customcmd>python main.py policyresult --help
Usage: main.py policyresult [OPTIONS]
This is test command
Options:
-fl, --file-loc OPEN Path to the file [required]
--help Show this message and exit.
3) This is how I converted the procedural code of abc-command.py code:
class policyresult():
def __init__(self):
pass
#click.command()
#click.option("--file-loc", '-fl', type=open, required=True, default=None, help="Path to the file")
def cli(self,file_loc):
"""
This is test command
"""
print("Path to file {}".format(file_loc))
obj = policyresult()
obj.cli()
Output of above code doesn't match with the previous output when the code was procedural in abc-command.py:
Here i am calling the main.py
(automation) K:\Pythonenv\automation\customcmd>python main.py
Usage: main.py [OPTIONS]
Try "main.py --help" for help.
Error: Missing option "--file-loc" / "-fl".
In the above output you can see it is directly going into the sub-command options things and giving error as well.
As far as i understand list_commands() which is in main.py can't list out the commands, this part i can't understand why it is not working properly.
I tried various things but couldn't find the proper way to implement OOP's in abc-command.py because of that my ouput doesn't match.
I am new to this click framework, please suggest any new changes in my approach if needed.
please look into this, sorry for this weird way to explaining this.
abc-command.py is evaluated before click parses command options because of this line in the file invoking the cli method:
obj.cli()
Also, in the get_command method implemented for the multi-command, commands are supposed to expose a 'cli' name in their namespace.
To fix this error, in abc-command.py update the line invoking the cli command with:
cli = obj.cli
so that a cli name is exposed in abc-command.py module

Pytest - How can I pass in a file from the command line for preliminary setup without setting up a test

I have a testscript setup in pycharm and I want to import a file to do some preliminary work. While this code works in python I'm getting errors in pytest.
parser = argparse.ArgumentParser()
parser.add_argument('file', nargs='+', help='properties file')
args_namespace = parser.parse_args()
args = vars(args_namespace)['file']
thisdir = os.path.dirname(os.path.abspath(__file__))
config = configparser.ConfigParser()
config.read(os.path.join(thisdir, args[0]))
However, when I run this with pytest, the error is:
ERROR: not found: /path/to/file/properties
(no name '/path/to/file/properties.ini' in any of [])
I believe what's happening is that pytest is trying to interpret this as it's own command rather than as something I'm passing in. Is there I way I can bring in the file without having to setup a specific pytest method and using the regular python argsparse library?

Parse commandline args in unittest python

I am creating a test case in python using unittest module.
I did create a parsing argument list that i want to get from user.
But when i use that argument while executing the python script, it gives error: "option -i not recognized
Usage: testing.py [options] [test] [...]"
code snippet:
class Testclass(unittest.TestCase):
#classmethod
def setUpClass(cls):
print "Hello Class"
def test_addnum(self):
print "Execute the test case"
#parser = parse_args(['-i'])
print 'simple_value =', args.inputfile
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('-i', help='input file', dest='inputfile')
ns, args = parser.parse_known_args(namespace=unittest)
#args = parser.parse_args()
return ns, sys.argv[:1] + args
if __name__ == '__main__':
unittest.main()
The error m getting on executing the above script with -i somefile.txt is:
option -i not recognized
Usage: testing.py [options] [test] [...]
Options:
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
-f, --failfast Stop on first failure
-c, --catch Catch control-C and display results
-b, --buffer Buffer stdout and stderr during test runs
Examples:
testing.py - run default set of tests
testing.py MyTestSuite - run suite 'MyTestSuite'
testing.py MyTestCase.testSomething - run MyTestCase.testSomething
testing.py MyTestCase - run all 'test*' test methods
in MyTestCase
Any help would be appreciated.
This script captures the -i command, while still allowing unittest.main to do its own commandline parsing:
import unittest
class Testclass(unittest.TestCase):
#classmethod
def setUpClass(cls):
print "Hello Class"
def test_addnum(self):
print "Execute the test case"
#parser = parse_args(['-i'])
print 'simple_value =', args.inputfile
import argparse
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('-i', help='input file', dest='inputfile')
ns, args = parser.parse_known_args(namespace=unittest)
#args = parser.parse_args()
return ns, sys.argv[:1] + args
if __name__ == '__main__':
import sys
args, argv = parse_args() # run this first
print(args, argv)
sys.argv[:] = argv # create cleans argv for main()
unittest.main()
produces:
1113:~/mypy$ python stack44236745.py -i testname -v
(<module 'unittest' from '/usr/lib/python2.7/unittest/__init__.pyc'>, ['stack44236745.py', '-v'])
Hello Class
test_addnum (__main__.Testclass) ... Execute the test case
simple_value = testname
ok
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
It looks rather kludgy, but does seem to work.
The idea is to run your own parser first, capturing the -i input, and putting the rest back into sys.argv. Your definition of parse_args suggests that you are already trying to do that.
Thanks hpaulj, your solution really helped me and I found one more solution for this problem. Hope it helps someone else facing the same issue.
import unittest
import argparse
import sys
class Testclass(unittest.TestCase):
#classmethod
def setUpClass(cls):
print "Hello Class"
def test_addnum(self):
print "Execute the test case"
#parser = parse_args(['-i'])
print 'simple_value =', args.inputfile
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-i', help='input file', dest='inputfile')
parser.add_argument('unittest_args', nargs='*')
args = parser.parse_args()
sys.argv[1:] = args.unittest_args
unittest.main()
Now executing the script with option -i as python testing.py -i somefile.txt gives result as
Hello Class
Execute the test case
simple_value = somefile.txt
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
Your code is setting up your argument parser using
argparse.ArgumentParser()
parser.add_argument('-i', help='input file', dest='inputfile')
in a method of your test class. But I see no indication that the code is actually calling the method.
So at the time you start the program, the parser does not yet exist, because the method TestClass.parse_args() hasn't been called yet.
Move the creation of the parser and specification of its parameters out of the class so that the code calls it when the program starts.
You are running unittest.main() as the main program. So it's complaining rightfully that it does not know about the option you wrote:
"option -i not recognized"
If you want to create your own test suite launcher you should look into
TextTestRunner

Categories

Resources