I'm running unit tests for my program.py file. I found an error: python3 -m unittest: error: unrecognized arguments:
.
I believe the error comes from the argparse
library I'm using where the target code is expecting some arguments.
Target file: program.py:
import argparse
parse = argparse.ArgumentParser(description="Command line program.")
parse.add_argument("--key", type=str,
help="Enter key")
parse.add_argument("--output", type=str,
help="Path to place results.")
args = parse.parse_args()
def program():
# Use args here
def writefile():
# Uses args and write to file
if __name__ == "__main__":
program()
Test file: program_test.py:
import unittest
import program
class TestProgram(unittest.TestCase):
def setUp(self):
self.argv_list = ["--key", "somefile.txt",
"--output", "myresultfile.txt"]
def test_program_stuff(self):
# See "Things I've tried"
program.writefile(...)
Command:
me@mylinux:myprogram$ env/bin/python3 -m unittest -v program_test.py
usage: python3 -m unittest [-h] [--key KEY] [--output OUTPUT]
python3 -m unittest: error: unrecognized arguments: -v program_test.py
Things I've tried:
argparse.Namespace
with argparse.Namespace(key="key.txt", output="result.txt")
sys.args
in test_program_stuff by sys.args.append(self.argv_list)
I've looked at solutions to unit testing argparse but none have helped so I'm thinking it may not be the same issue:
Note: I do realize that this is a duplicate of Pytest unrecognized arguments when importing file with argparse, however, that question was unfortunately not answered. It is also a duplicate of How to call function with argparse arguments in unittests?, however, he doesn't want to provide arguments and instead wants to call another function defined in the module.
Essentially, the problem can be reduced to the following:
# main.py
import argparse
parse = argparse.ArgumentParser()
parse.add_argument("--foo", action="store_true")
args = parse.parse_args()
and
# tests.py
import main
If we run that with python -m unittest ./tests.py
, we receive the following output:
usage: python -m unittest [-h] [--foo FOO]
python -m unittest: error: unrecognized arguments: ./tests.py
The problem is that if you import something, all the top level code will run during the import. Usually, this isn't a problem because in a library you only really define functions to be used by other programs, however, in your case the parse.parse_args()
runs.
This can be resolved by guarding this logic similar to what you already did:
import argparse
def main():
parse = argparse.ArgumentParser()
parse.add_argument("--foo")
args = parse.parse_args()
if __name__ == "__main__":
main()
Here, __name__
will contain the name of the module which would be "main"
if it is imported or "__main__"
if it is run directly. Therefore, the main()
function will not be called during the unit test. Read more about this here.
However, in your specific case, it seems that you want to test the functionality of the main()
function (or rather the code that you have in your main
module.)
There are generally two ways to achieve this:
You can simply accept the arguments in main()
:
import argparse
import sys
def main(argv):
parse = argparse.ArgumentParser()
parse.add_argument("--foo", action="store_true")
args = parse.parse_args(argv[1:])
if __name__ == "__main__":
main(sys.argv)
Then you are able to provide these arguments in the unit test as well:
import unittest
import main
class Tests(unittest.TestCase):
def test_whatever(self):
main.main(["main.py", "--foo"])
You can use a mock-framework to change the value of sys.argv
. In this situation would seem a bit over-engineered. However, if you are interested, that is answered here.