How to deal with global variables across modules in pytest? #11982
-
Hi PyTest Community, I hope someone can help me with the following problem: I wrote a small CLI package and I wanted to simulate the input of the CLI with pytest. src/cli/config.py config = {
"format" : False,
"preset_format" : False,
"file" : "./save.file"
} src/cli/main.py import argparse
from . import process
from .config import config
def main():
parent_parser = argparse.ArgumentParser(add_help=False)
fmt_group = parent_parser.add_argument_group("Format Options", "Special Formating Options")
fmt_group.add_argument("--format")
fmt_group.add_argument("--preset_format")
fmt_group.add_argument("--file", default="./save.file")
parser = argparse.ArgumentParser(prog="test", description=f"test parser")
subparser = parser.add_subparsers(title="Data Source", dest="cmd_parsing", required=True)
parser_option1 = subparser.add_parser("csv", help="Option 1", parents=[parent_parser], add_help=False)
parser_option1.set_defaults(func=process.csv_analysis)
parser_option2 = subparser.add_parser("md", help="Option 2", parents=[parent_parser], add_help=False)
parser_option2.set_defaults(func=process.md_analysis)
args, unknown = parser.parse_known_args()
if args.format != None:
config["format"] = True
if args.preset_format != None:
config["preset_format"] = True
config["file"] = args.file
args.func()
if __name__ == "__main__":
main() src/cli/process.py from .config import config
from . import format
def csv_analysis():
#read file
do = "stuff"
if config["preset_format"]:
ignore = "something"
format.preset_format(do )
else:
format.format(do)
def md_analysis():
#read file
do = "stuff"
#Presets not allowed
format.format(do) src/cli/format.py from .config import config
def file_save(data: str):
with open(config["file"], "w") as f:
f.write(data)
def format(data: str):
data += "\n\nformated stuff\n"
print(data)
def preset_format(data: str):
data += "\n\npresets applied\n"
print(data) As I said in the beginning. I wanted to simulate the input and test the complete chain (manipulate config, process, format). What I noticed is, that the fixture is only modified in the module cli.main, but it gets reset to the fixture in other modules like cli.process or cli.format. I also do not want to implement a subprocess mechanism, because then I will lost track about the code coverage. This is my current test_main.py, but I am at a point where I don't know how to solve this problem. tests/test_main.py import sys
import pytest
import cli.main as cli
@pytest.fixture(scope="function", )
def reset_conf():
config = {
"format" : False,
"preset_format" : False,
"file" : "./save.file"
}
yield config
def test_main_csv_format(reset_conf):
sys.argv = ["myprogram", "csv", "--file", "./justatest", "-fmt", "format stuff"]
cli.config = reset_conf
cli.main()
config = cli.config
assert config["format"] == True
assert config["preset_format"] == False
def test_main_csv_format_preset(capsys, reset_conf):
sys.argv = ["myprogram", "csv", "--file", "./justatest", "-pfmt", "use preset"]
cli.config = reset_conf
cli.main()
cap = capsys.readouterr()
config = cli.config
assert config["preset_format"] == True
assert config["format"] == False
assert "presets applied" in cap.out This is just a minified example code of my real code. My project is more complicated and I did not want pass down a config object/variable through 10 function calls. Therefore I used a global variable. Thanks in advance for your help. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
The correct way is not to have globals, simply enabled the main function to take argv,then call it Any test that needs to manipulate sys.argv is broken |
Beta Was this translation helpful? Give feedback.
@RonnyPfannschmidt's answer is to the point, to complement, just change your code from:
To:
This way you can easily pass the argument directly on each test, instead of manipulating
sys.argv
.You will also need to do the same for the configuration and each function.
This pattern of "passing things instead of accessing globals" is considered good practice and aids in testing. Using globals might seem convenient at first, but globals have many drawba…