pytest testing internal functions - python

Let's say that I have a package qaz:
qaz/
__init__.py
qaz.py
tests/
test_qaz.py
setup.py
Now, I want to test some internal qaz.py functions:
def _abc():
return 3
def def():
return _abc() + 2
but when I run pytest with tests like this:
from qaz.qaz import *
def test_abc():
assert _abc() == 3
def test_def():
assert def() == 5
My question is how to test _abc()?
Now I'm getting:
E NameError: name '_abc' is not defined

Oh, figured it out. You need to be specific, when it comes to the internal functions:
from qaz.qaz import _abc

Related

Change XDG_DATA_HOME environment variable in pytest

I have some trouble with changing the environment variables with tmp_path in a project, so I tried to write a sample project to debug it. That doesn't work and don't understand why. The project uses a settings.py file to define some constants. module.py import this constants and do his stuff.
src
settings.py
import os
from pathlib import Path
XDG_HOME = Path(os.environ.get("XDG_DATA_HOME"))
HOME = XDG_HOME / "home"
module.py
from xdg_and_pytest.settings import HOME
def return_home(default=HOME):
return default
tests
In my tests, I have a fixture to change the environment variable. The first test to call it put tmp_dir in the $XDG_DATA_HOME environment variable but the second one get the same path ...
conftest.py
import pytest
#pytest.fixture
def new_home(tmp_path, monkeypatch):
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
return new_home
test_module.py
def test_new_home_first(new_home):
from xdg_and_pytest.module import return_home
assert "new_home_first" in str(return_home())
def test_new_home_second(new_home):
from xdg_and_pytest.module import return_home
assert "new_home_second" in str(return_home())
command-line result
poetry run pytest
====================== test session starts ======================
platform linux -- Python 3.10.4, pytest-7.1.2, pluggy-1.0.0
collected 2 items
tests/test_module.py .F [100%]
=========================== FAILURES ============================
_____________________ test_new_home_second ______________________
new_home = <function new_home at 0x7f75991b13f0>
def test_new_home_second(new_home):
from xdg_and_pytest.module import return_home
> assert "new_home_second" in str(return_home())
E AssertionError: assert 'new_home_second' in '/tmp/pytest-of-bisam/pytest-92/test_new_home_first0/home'
E + where '/tmp/pytest-of-bisam/pytest-92/test_new_home_first0/home' = str(PosixPath('/tmp/pytest-of-bisam/pytest-92/test_new_home_first0/home'))
E + where PosixPath('/tmp/pytest-of-bisam/pytest-92/test_new_home_first0/home') = <function return_home at 0x7f759920bac0>()
tests/test_module.py:10: AssertionError
==================== short test summary info ====================
FAILED tests/test_module.py::test_new_home_second - AssertionE...
================== 1 failed, 1 passed in 0.09s ==================
This is the clearest code I got, but I tried lots of different monkeypatching ways. Maybe should I left the idea of a settings.py file and try something else ? I don't want to use a scope=session solution because I want to try different kind of data in $XDG_DATA_HOME.

Mock a function present inside a list in pytest

I want to mock a function present inside a list and check whether it has been called at least once. Below is a similar implementation I tried:-
In fun_list.py (funA and funB are two functions in other_module)
import other_module
FUN_LIST = [
other_module.funA,
other_module.funB,
]
def run_funs():
for fun in FUN_LIST:
fun()
In demo.py
from fun_list import run_funs
def run_demo():
...
run_funs()
...
In test_demo.py
from demo import run_demo
#patch('other_module.funB')
def test_demo_funs(mocked_funB):
mocked_funB.return_value = {}
run_demo()
assert mocked_funB.called
In above case, I'm trying to mock funB in other_module but the function doesn't get mocked and the cursor gets inside the actual funB in other_module. Thus, the assert mocked_funB.called returns false.
Any lead on how I can mock other_module.funB ?
I have found a similar question on StackOverflow but that went unanswered, so decided to post my version of it.
Any help will be appreciated, thank you in advance.
You need to mock before importing the module under test. The code in the module scope will be executed when import the module. It is too late to mock through the decorator when the test case is executed.
E.g.
other_module.py:
def funA():
pass
def funB():
pass
fun_list.py:
import other_module
print('execute module scope code')
FUN_LIST = [
other_module.funA,
other_module.funB,
]
def run_funs():
for fun in FUN_LIST:
fun()
demo.py:
from fun_list import run_funs
def run_demo():
run_funs()
test_demo.py:
import unittest
from unittest.mock import patch
class TestDemo(unittest.TestCase):
#patch('other_module.funB')
def test_demo_funs(self, mocked_funB):
print('mock before import the module')
from demo import run_demo
mocked_funB.return_value = {}
run_demo()
assert mocked_funB.called
if __name__ == '__main__':
unittest.main()
test result:
mock before import the module
execute module scope code
.
----------------------------------------------------------------------
Ran 1 test in 0.002s
OK
Name Stmts Miss Cover Missing
--------------------------------------------------------------------------
src/stackoverflow/67563601/demo.py 3 0 100%
src/stackoverflow/67563601/fun_list.py 6 0 100%
src/stackoverflow/67563601/other_module.py 4 1 75% 6
src/stackoverflow/67563601/test_demo.py 12 0 100%
--------------------------------------------------------------------------
TOTAL 25 1 96%
I took the lead from #slideshowp2's answer and have modified things a bit differently. In my case I was having multiple such test functions that is mocking funB and calling run_demo (originally a client.post() call from Django.test). If an earlier function calls it successfully, the other subsequent patches were failing (because of the same reason stated by #slideshowp2). So, I changed the approach to this :-
In fun_list.py (funA and funB are two functions in other_module)
import other_module
FUN_LIST = [
'funA',
'funB',
]
def run_funs():
for fun in FUN_LIST:
getattr(other_module, fun)()
In demo.py
from fun_list import run_funs
def run_demo():
...
run_funs()
...
In test_demo.py
from demo import run_demo
#patch('other_module.funB')
def test_demo_funs(mocked_funB):
mocked_funB.return_value = {}
run_demo()
assert mocked_funB.called

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

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

Mocking function imported at the top of the file

I am trying to do a test in a project, and I am having an weird error.
I reproduced the very similar situation with the toy example below:
This is the file structure:
.
├── some_package
│ ├── __init__.py
│ └── some_file.py
└── test_mock_patch.py
"""some_package/some_file.py"""
# when I import here, the test fails
from math import floor
def some_func(a, b):
# if I import here, the test passes
# from math import floor
return floor(a + b)
"""test_mock_patch.py"""
import pytest
from unittest import mock
from some_package.some_file import some_func
#pytest.fixture
def mock_floor():
with mock.patch('math.floor', autospec=True) as m:
yield m
def test_some_func(mock_floor):
some_func(1.1, 1)
assert mock_floor.call_count == 1
Command used: pytest -v -s test_mock_patch.py
The error:
Why when I import inside the function the test_some_func passes and when I import at the top the test fails?
Thank you in advance for any help to explain this behaviour of mock.patch
Versions:
Python 3.7.3
pytest 4.4.1
Here is a minimal example how to achieve the desired result by changing your test_mock_patch.py file.
import pytest
from some_package.some_file import some_func
def test_some_func(monkeypatch):
with monkeypatch.context() as mc:
mc.setattr('some_package.some_file.floor', lambda x: 'foo')
res = some_func(1.1, 1)
assert res == 'foo'
Like I mentioned in the comments, you need to patch the function where it is being imported.

NameError: Global Name 'test' is not defined

Hi I have two python files: project.py and test.py.
I am trying to import variable from test.py to project.py.
Following is the code:
test.py
newton = 0
def apple(n):
global newton
n = newton
time.sleep(15)
n = n + 1
return
and in project.py
from test import *
class search(object):
def __init__(self):
self.servo = test.apple(n)
def run(self):
while (self.servo < 1):
print "hELLO"
When I run project.py I get NameError: Global name 'test' is not defined in project.py self.servo = test.apple(n)
Can anyone point out what is wrong in my code?
What are you expecting test to be?
from test import *
This will load everything found in test, which in this case is test.py. So that will load the following into the global namespace:
newton (with a value of 0)
apple (a function)
It does not load any symbol named test, so when you call test.apple in your __init__ method, you get a NameError.
If you want to import test.py as test, you instead need to just import the module itself, not import things from the module:
import test

Categories

Resources