Why is my constant mock with python unit test not working - python

I use some mocking code to mock a constant in python. I read the docs over and over again but I don't see what I do wrong:
from unittest import TestCase, mock
from sync_servicenowjira.adapter_sn_to_jira import adapt_sn_to_jira
from sync_servicenowjira.constants import DOWNLOAD_PATH
from sync_servicenowjira.dao_service_now import get_service_now_tickets
# #mock.patch('sync_servicenowjira.constants.DOWNLOAD_PATH', './test_files/')
class Test(TestCase):
def test_adapt_sn_to_jira(self):
with mock.patch('sync_servicenowjira.constants.DOWNLOAD_PATH', './test_files/'):
test = DOWNLOAD_PATH
print(test)
sn_tickets = get_service_now_tickets(True)
jira_tickets = adapt_sn_to_jira(sn_tickets)
self.assertIsInstance(jira_tickets, list)
self.assertGreater(len(jira_tickets), 1)

Related

Unittest mock: return function which receives one pytest fixture and one argument

I'm creating some unit tests and using pytest fixtures in combination with some unittest.mock patch.object calls.
I would like to reuse a function that is called by some of my tests. It makes use of a pytest fixture (specified as the first "argument" of the function) and it requires an additional argument. It looks something like this:
import pandas as pd
import pytest
import os
from unittest.mock import patch
#pytest.fixture()
def rootdir():
return os.path.dirname(os.path.abspath(__file__))
def my_mock_ret(rootdir, number):
print(f"{rootdir}_{number}")
return f"{rootdir}_{number}"
def test_simple(rootdir):
a = pd.DataFrame()
with patch.object(a, 'to_csv', lambda x: my_mock_ret(rootdir, x)) as _:
a.to_csv(rootdir)
The tricky part is how to pass the number argument to my_mock_ret while also being able to access the rootdir fixture from inside it.
I've tried this way using lambda but it does not work.
Edit
It works if y put my_mock_ret inside test_simple, but I don't want to do that because I want to reuse my_mock_ret for several other tests:
import pandas as pd
import pytest
import os
from unittest.mock import patch
#pytest.fixture()
def rootdir():
return os.path.dirname(os.path.abspath(__file__))
def test_simple(rootdir):
def my_mock_ret(number):
print(f"{rootdir}_{number}")
return f"{rootdir}_{number}"
a = pd.DataFrame()
with patch.object(a, 'to_csv', my_mock_ret) as _:
a.to_csv(rootdir)
What you need right here, is the factory pattern:
import pandas as pd
import pytest
import os
from unittest.mock import patch
#pytest.fixture()
def rootdir():
return os.path.dirname(os.path.abspath(__file__))
#pytest.fixture()
def my_mock_ret(rootdir):
def _my_mock_ret(number):
print(f"{rootdir}_{number}")
return f"{rootdir}_{number}"
return _my_mock_ret
def test_simple(my_mock_ret, rootdir):
a = pd.DataFrame()
with patch.object(a, 'to_csv', my_mock_ret) as _:
a.to_csv(rootdir)
here my_mock_ret will create a function, which captures rootdir, and can take the number argument.

Better way to import modules in pytest

I need a cleaner way to import the modules into the test_file.py, I am using pytest for my test. My present pytest setup is like the below which works, but I need a more organised way to import the modules into the test functions without initializing the database client. The global variable is breaking my test when I import using the cleaner way.
conftest.py
import pytest
#pytest.fixture(autouse=True)
def no_request_cassandra_client(mocker):
"""Remove cassandra.cluster.Cluster for all tests."""
mock_cass_cluster = mocker.patch("cassandra.cluster.Cluster")
test_file.py
import pytest
def test_car_class(no_request_cassandra_client):
from A.BMW import car
car_func = car()
assert car_func
This is what I want to have, but the car method has a global variable like the BMW.py, the test is breaking because of the Cluster(["Cluster_ip_address"]) with the cleaner solution.
Cleaner test_file.py
import pytest
from A.BMW import car
def test_car_class():
car_func = car()
assert car_func
BMW.py
from cassandra.cluster import Cluster
cluster = Cluster(["Cluster_ip_address"])
def car():
x = 2 + 3
print(x)
return x

How to check if requests.get() is called in a method using unittest.mock

I am struggling to wrap my head around the unittest.mock docs and the many examples on SO.
I have a class with a method:
my_class.py
import requests
class MyClass:
def do_stuff(do_it=True):
if do_it:
requests.get('https://someapi.com')
And I want a unit test that checks that do_stuff tries to call the api using unittest.mock
I have a test (very simplified):
test_my_class.py
from django.test import TestCase
class TestMyClass(TestCase)
def test_do_stuff_uses_api(self):
MyClass().do_stuff()
# How to assert that requests.get() method was called once?
Checkout the package requests_mock
https://requests-mock.readthedocs.io/en/latest/history.html#called
from django.test import TestCase
import requests_mock
class TestMyClass(TestCase)
def test_do_stuff_uses_api(self):
with requests_mock.mock() as m:
m.get('https://someapi.com', json={....})
MyClass().do_stuff()
history = m.request_history
self.assertEqual(len(history), 1, "Should have been called once")

Check if pytest fixture is called once during testing

Does pytest provides functionality like unittest.mock to check if the mock was actually called once(or once with some parameter)?
Sample Source code:
my_package/my_module.py
from com.abc.validation import Validation
class MyModule:
def __init__(self):
pass
def will_call_other_package(self):
val = Validation()
val.do()
def run(self):
self.will_call_other_package()
Sample test code for the above source code:
test_my_module.py
import pytest
from pytest_mock import mocker
from my_package.my_module import MyModule
#pytest.fixture
def mock_will_call_other_package(mocker):
mocker.patch('my_package.my_module.will_call_other_package')
#pytest.mark.usefixtures("mock_will_call_other_package")
class TestMyModule:
def test_run(self):
MyModule().run()
#check `will_call_other_package` method is called.
#Looking for something similar to what unittest.mock provide
#mock_will_call_other_package.called_once
If you want to use a fixture that does the patching, you can move the patching into a fixture:
import pytest
from unittest import mock
from my_package.my_module import MyModule
#pytest.fixture
def mock_will_call_other_package():
with mock.patch('my_package.my_module.will_call_other_package') as mocked:
yield mocked
# the mocking will be reverted here, e.g. after the test
class TestMyModule:
def test_run(self, mock_will_call_other_package):
MyModule().run()
mock_will_call_other_package.assert_called_once()
Note that you have to use the fixture parameter in the test. Just using #pytest.mark.usefixtures will not give you access to the mock itself. You can still use it to be effective in all tests in the class, if you don't need to access the mock in all tests (or use autouse=True in the fixture).
Also note that you don't need pytest-mock here - but as mentioned by #hoefling, using it makes the fixture better readable, because you don't need the with clause :
#pytest.fixture
def mock_will_call_other_package(mocker):
yield mocker.patch('my_package.my_module.will_call_other_package')
As an aside: you don't need to import mocker. Fixtures are looked up by name, and available automatically if the respective plugin is installed.
You could try this:
import pytest
from my_package.my_module import MyModule
def test_run(mocker):
mocker.patch('my_package.my_module.will_call_other_package')
MyModule().run()
mock_will_call_other_package.assert_called_once()
First of all, you may not need the burden of an external library such as pytest_mock, because pytest already got you covered using the integration with unittest.
You also do not need to use the usefixtures because whenever you need a fixture, you just receive it in your test method.
An ideal scenario based on your own code would look similar to this:
import pytest
from unittest.mock import patch
from com.abc.validation import Validation
class MyModule:
def __init__(self):
pass
def will_call_other_package(self):
val = Validation()
val.do()
def run(self):
self.will_call_other_package()
#pytest.fixture
def call_other_module():
with patch("my_package.my_module.MyModule.will_call_other_package") as _patched:
yield _patched
class TestMyModule:
def test_run_will_call_other_package(self, call_other_module):
call_other_module.assert_not_called()
obj = MyModule()
call_other_module.assert_not_called()
obj.run()
call_other_module.assert_called_once()
And also if you want to make sure that you did infact patch the target MyModule.will_call_other_package, modify your test like this:
class TestMyModule:
def test_run_will_call_other_package(self, call_other_module):
call_other_module.assert_not_called()
obj = MyModule()
call_other_module.assert_not_called()
obj.run()
call_other_module.assert_called_once()
assert False, (MyModule.will_call_other_package, call_other_module)
And you'll see something similar to this:
AssertionError: (<MagicMock name='will_call_other_package' id='140695551841328'>, <MagicMock name='will_call_other_package' id='140695551841328'>)
As you can see the id of both objects are the same, confirming our experiment was successful.

How to mock the get function from requests.session?

I am trying to mock the get function from requests.session and somehow it does not end up happening.
I have the following code:
#main.py
import requests
def function_with_get():
c = requests.session()
c.get('https://awesome_url.com')
# do some other stuff
return c
def second_function_with_get(client):
c.get('https://awesome_url.com')
# do some other stuff
#test.py
from unittest import mock
from django.test import TestCase
class Testing(TestCase):
#mock.patch('main.requests.session.get)
#mock.patch('main.requests.session)
def test_which_fails_because_of_get(mock_sess, mock_get):
client = function_with_get()
second_function_with_get(client)
assertEqual(mock_requests_session_get.call_count, 2)
The test throws an assertion error that mock_get is called 0 times (0 != 2)
How should the get function of requests.session() be mocked?
It seems that you are already mocking the requests session - since it is a MagicMock, you don't need to additionally mock the get method itself - checking for calls on the session will be enough.
So, your test.py could look like this:
#test.py
from unittest import mock
from unittest import TestCase
from main import function_with_get, second_function_with_get
class Testing(TestCase):
#mock.patch('main.requests.session')
def test_which_fails_because_of_get(self, mock_sess):
client = function_with_get()
second_function_with_get(client)
self.assertEqual(mock_sess.return_value.get.call_count, 2)
You could also try to create your own SessionMock class with the get method mocked, but it would require also proper setting (or resetting) it for each test. Personally, I usually find using MagicMock and its return_value chain easier.

Categories