How to reference local import when patching - proper python testing structure - python

I have a Starlette app and I'm attempting to perform end to end testing on the entire application. A function defined in a.py is called by a function in b.py through a local import, and for testing purpose I would like to replace the function with a self defined function when testing.
Like others, I'm running into issues getting the path string to work. After looking at existing questions on stackoverflow, I think I'm supposed to be patching the reference in b.py, but run into the following error AttributeError: <function b at 0x7f5298087af0> does not have the attribute 'a'
Here's the relevant structure and code
Project_folder
- app
- lib
+ __init__.py
+ a.py
+ b.py
- handle
+ __init__.py
- test
+ test.py
+ main.py
#a.py
def a(id):
return stuff
#b.py
from .a import a
def b():
return a(some_stuff)
#lib.__init__py
from .a import a
from .b import b
routes refers to an async function which calls b somewhere in the code
#main.py
from starlette.applications import Starlette
from handle import routes
app = Starlette(routes=routes)
My attempt at test.py
from starlette.testclient import TestClient
from main import app
import pytest
from mock import patch
client = TestClient(app)
def mock_a(id):
return 'some value'
#patch('app.lib.b.a', new=mock_a)
def test_app(request):
response = client.post('/route', request)
assert response.status_code == 200
I'm very new to mocks and patching, and would appreciate any advice on how I should be setting this up

You import "a" and "b" as functions in init.py. So patch libs, not libs.a or libs.b. if you want to patch inside the module itself the take the imports out of init.py and the you can do something like this:
import libs
libs.b.b = libs.a.a

Related

issue with nested python module importing when executing imported function itself

I believe this question must have already been asked but I cannot find an explanation for my problem, sorry if it is a duplicate.
Folder
├── Generator.py
└── modules
├── Function1.py
└── Subfunction.py
Generator.py imports Function1, and Function1 imports Subfunction.
Function1 must be able to be run as a standalone program and as an imported module of Generator.py
It is not a problem itself, as I am using the if __ name__ == "__ main__": to recognize the call type.
But the program fails on importing Subfunction depending on the code I am executing.
# Generator.py
import Function1
# Function1.py
import Subfunction
import modules.Subfunction
The first one works if I execute Function1.py, but it fails if I run Generator.py
The second one works if I execute Generator.py, but it fails if I run Function1.py
I thought imports and relative paths are related to the module where the code is placed, not from a perspective of the top-caller. I tried import .modules.Function1 and import .Function1 but the issue remains.
Is there any elegant way to import Subfunction for both uses, or do I need to include import under if name == main or trap it in try/except?
Edit: all code for #Bastien B
In this shape it works if I execute Function1.py itself.
If I execute Generator.py, I get the ModuleNotFoundError: No module named 'Function1'
# Generator.py
import Function1
print(Function1.Function1_return)
# Function1.py
def Function1_return():
return Subfunction.Subfunction_return()
import Subfunction
if __name__ == '__main__':
print(Function1_return())
# Subfunction.py
def Subfunction_return():
return "this is subfunction"
From what i can see your 'if name ...' is not the problem.
If you use a local venv this should work just fine:
generator.py
from Folder.modules.function1 import Function1_return
print(Function1_return())
Function1.py
from Folder.modules.Subfunction import Subfunction_return
def Function1_return():
return Subfunction_return()
if __name__ == '__main__':
print(Function1_return())
Subfunction.py
def Subfunction_return():
return "this is subfunction"
Depending of your files structure you may have to tweak this a beat

How to import library modules to my namespace from another module?

I have written this function inside my utils module
def megaimport(library_name):
skip_modules = ["sys", "os", "dir_path"]
exec(f"import {library_name}")
for module in dir(eval(library_name)):
if module.startswith("__") == False and module not in skip_modules:
exec(f"from {library_name}.{module} import *", globals(), locals())
What I am trying to achieve is to import all functions to my namespace from any library with the following structure:
library:
- module1.py
- function_1_1
- function_1_2
- module2.py
- function_2_1
- function_2_2
just by running in my notebook.ipynb:
from utils import megaimport
megaimport("library")
function_1_2()
function_2_1()
... etc
The idea is to dynamically call from library.module1 import *for every module in a library.
When I do this the function runs without errors but I when I call the functions they have not been imported properly. However, if I define the function in my code and run it it works as expected.
How can I import all the functions into my current namespace?
I am aware that importing everything like this is bad practice but for me it is convenient when working with jupyter notebooks.
My notebook.ipynb and utils.py live in the same directory.
Ok, after trying for a while I realised that I should pass globals() as a parameter to the function and pass it over to the exec in order to make it work in my namespace.
The function now looks like this:
import importlib
def megaimport(globals_dict, library_name):
skip_modules = ["sys", "os", "dir_path"]
mod = importlib.import_module(library)
for module in dir(mod):
if module.startswith("__") == False and module not in skip_modules:
exec(f"from {library_name}.{module} import *", globals_dict)
And it can be called just by:
from utils import megaimport
megaimport(globals(),"library")

import on Python doesn't work as expected

Although the variable should be imported, I get "name X is not defined" exception.
main.py
from config import *
from utils import *
say_hello()
utils.py
from config import *
def say_hello():
print(config_var)
config.py
from utils import *
config_var = "Hello"
Trying to run "main.py":
Traceback (most recent call last):
File "main.py", line 3, in
say_hello()
File "C:\Users\utils.py", line 3, in say_hello
print(config_var)
NameError: name 'config_var' is not defined
What happened here? Why some_var is not accessible from utils.py?
You are importing config in util and util in config which will causing this error(create cross loop). remove from utils import * from config.py and then try this.
And in main.py you don't need to import the from config import * unless you are using variables from config directly in your main()
you should also import config.config_var, since this variable belongs to that specific module
You are creating to many import statements perhaps try the following below, but also you need to define a parameter in utils.py if you are passing a parameter through there.
In utils.py we require a parameter to be passed since you want to print out the appropriate value, In config.py you are defining a value. Then in main.py as discussed before using the wildcard operator "*" isn't entirely good in this situation then in order to call the respective functions you need to address them through their file name
In utils.py :
def say_hello(config_var):
print(config_var)
In config.py
config_var = "Hello"
Then in main.py
import config as cn
import utils as ut
ut.say_hello(cn.config_var)
Check out this thread for how to write python modules as well How to write a Python module/package?

Importlib.import_module will not import the module even though the param is the abs path

I have my .py module which is in C:\Python_Projects\MyModules\ with the name button_generator.py.
My code goes something like this:
module_path='C:\\Python_Projects\\MyModules'
module_name='button_generator.py'
sys.path.append(module_path)
try:
limp=importlib.import_module(module_name.split('.')[0])
except:
print 'module import error'
I have tried other versions aswell:
importlib.import_module(module_name) without the split
importlib.import_module('C:\Python_Projects\MyModules\button_generator.py')
importlib.import_module('C:\Python_Projects\MyModules\button_generator')
The folder C:\Python_Projects\MyModules is in my sys.path as I checked during debug.
Why wouldn't the module import?
I suggest you to reorder your project directories and avoid calling other modules which are not in your current directory project. You'll avoid those kind of errors.
For example, let's organize our project directories and folders to look something like this:
MyProjectFolder/
├── main.py
└── modules
├── __init__.py
└── MyLib.py
NB: Don't forget to add an empty file called __init__.py
MyLib.py :
#!/usr/bin/python3
class MyLib:
def __init__(self):
self.say_hello = "Hello i'm in modules/MyLib"
def print_say_hello(self):
print(self.say_hello)
main.py:
#!/usr/bin/python3
# from folder.file import class
from modules.MyLib import MyLib
class MainClass:
def __init__(self):
my_lib = MyLib() # load MyLib class
my_lib.print_say_hello() # access to MyLib methods
### Test
if __name__ == '__main__':
app = MainClass()
In terminal when i run:
$ python3 main.py
output:
Hello i'm in modules/MyLib
So here we have successfully imported the class in modules/MyLib.py into our main.py file.
I found the error:
After treating the ImportError exception by printing it's args, I noticed that button_generator.py had an Import that was not resolving. Basically, button_generator.py could not be imported because it had a wrong import.

Which module to patch

I have the following directory
/root
/app
/api
my_api.py
/service
my_service.py
/tests
test_api.py
my_api.py
import app
def run_service():
app.service.my_service.service_function()
test_api.py
#patch('app.service.my_service.service_function')
test_run_service(self,mock_service):
mock_service.return_value = 'Mock'
response = self.client.get(url_for('api.run_service')
self.assertTrue(response == expected_responce)
The above works. What I cant figure out, is which module I need to patch, in case I wanted to import service_function in my_apy.py like this:
from app.service.my_service import service_function
If I do the import like above, mock stops working.
You need to patch out app.api.my_api.service_function, since that is the global name already bound to the imported object:
#patch('app.api.my_api.service_function')
test_run_service(self, mock_service):
mock_service.return_value = 'Mock'
response = self.client.get(url_for('api.run_service')
self.assertTrue(response == expected_responce)
See the Where to patch section:
The basic principle is that you patch where an object is looked up, which is not necessarily the same place as where it is defined.

Categories

Resources