I have a unit test class that tests the same method twice - once with happy path and once with a failure. If I run both tests individually then they pass, but if I run them together then the patch return_value from the first test is also applied to the second, and so one test will fail. What am I missing here?
import unittest
from unittest import mock
from unittest.mock import Mock
class MainTest(unittest.TestCase):
def test_happy_path(self):
with mock.patch('google.cloud.bigquery.Client') as bq_patch:
bq_patch().insert_rows_json.return_value = None
import main
data = {'trigger': 'testval'}
req = Mock(get_json=Mock(return_value=data), args=data)
assert 200 == main.http_to_bq(req)
bq_patch.reset_mock()
req.reset_mock()
def test_bigquery_error(self):
with mock.patch('google.cloud.bigquery.Client') as bq_patch:
bq_patch().insert_rows_json.return_value = 'BigQuery connection error found'
import main
data = {'trigger': 'testval'}
req = Mock(get_json=Mock(return_value=data), args=data)
assert 500 == main.http_to_bq(req)
req.reset_mock()
I was concentrating on the patches when actually the problem was with import main. The patch is applied when main is imported for the first time, and subsequent import main calls do nothing. I imported:
from importlib import reload
and replaced both instances of
import main
with
import main
reload(main)
That allows main to be re-patched with the latest patch.
Related
pytest : How to write pytest code to detect a "func" called without actually executing the "func" ,
target.py:
import requests
import threading
CFG=None
targets={}
def add_target(func):
targets[func.__name__] = func
return func
def parent_func(url):
for funct in set(targets):
threading.Thread(target=targets[funct], args=(CFG[url],)).start()
#add_target
def child_func(url):
try:
response = requests.request("POST", url,
headers={"Content-Type": "application/json"},
data="{\"text\": \"target py test\"}")
response.raise_for_status()
except Exception as err:
raise RuntimeError(err)
test_target.py:
During testing, I want child_func() to get called but the child_func() body should not get executed. Rather, after running tests, child_func() gets executed & test run results in "AssertionError: False is not true "
from unittest.mock import Mock, patch
import target
# Third-party imports...
from nose.tools import assert_true, assert_is_not_none
target.CFG={'url':"some valid url"}
#patch('target.child_func')
def test_child_func(mock_child_func):
parent_func("url")
assert_true(mock_child_func.called)
First (for the sake of my own sanity) let's whittle this down to an actual MRE -- we don't actually need threading or requests or any of the implementation details of child_func to demonstrate the problem here.
target.py:
targets={}
def add_target(func):
targets[func.__name__] = func
return func
def parent_func(url):
for func in targets.values():
func(url)
#add_target
def child_func(url):
raise NotImplementedError("The test shouldn't actually call this function!")
test_target.py:
from unittest.mock import Mock, patch
from target import parent_func
#patch('target.child_func')
def test_parent_func(mock_child_func):
parent_func("url")
assert mock_child_func.call_count == 1
Within our test, we want parent_func to call mock_child_func, not the real child_func. But when we run our test, we can quickly see that our patch didn't work:
============================================== short test summary info ===============================================
FAILED test_target.py::test_parent_func - NotImplementedError: The test shouldn't actually call this function!
The reason for this is that parent_func doesn't call child_func, it calls targets['child_func']. Patching 'target.child_func' doesn't modify the actual function, it modifies what the name child_func points to in the target module -- and that name isn't in use in the code under test!
The easiest fix here is to patch target.targets:
from unittest.mock import Mock, patch
from target import parent_func
#patch('target.targets', new_callable=dict)
def test_parent_func(mock_targets):
mock_targets['child_func'] = Mock()
parent_func("url")
assert mock_targets['child_func'].call_count == 1
Now our test passes because when parent_func iterates through its targets, it gets our mock_targets dict, which we can populate with whatever functions/mocks we want.
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.
I am trying to perform unit tests using Pytest on some of my code. The tests are being run in a separate Conda environment on a Docker. I would like to test certain functionalities of my code but cannot install all the modules of my code, because of the complexity of the installation of some of these modules and the time it would take to run.
How can I import only certain modules from a file, without needing the other modules installed?
If I try running a test, whilst importing a module from a file, my test fails since it cannot import the other modules.
Below is a mock-up of my file system:
test_file.py
from other_file import afunction
def this_test():
assert afunction(2, 2) == 4
other_file.py
import math
import large_program
def afunction(x,y):
return math.pow(x, y)
def anotherfunc():
return large_program()
If I run Pytest, I will get:
E ImportError: No module named 'large_program'
Quite simply: extract the functions that do not depend on large_program into another module and only test this module. Note that you can do this without breaking client code (code depending on your other_file module) by importing the relevant names in other_file:
# utils.py
import math
def afunction(x,y):
return math.pow(x, y)
then
# other_file.py
import large_program
# this will allow client code to access `afunction` from `other_file`
from utils import afunction
def anotherfunc():
return large_program()
and finally:
# test_file.py
# here we import from utils so we don't depend on `large_program`
from utils import afunction
def this_test():
assert afunction(2, 2) == 4
I liked the idea of cristid9 of mocking and combined it with dano's post here. I created an empty file called "nothing.py" that will replace the "large_program" with a unittest mock:
test_file.py
import unittest.mock as mock
import nothing
with mock.patch.dict('sys.modules', large_program=nothing):
from other_file import afunction
def this_test():
assert afunction(2, 2) == 4
The other_file.py looks still like this
import math
import large_program
def afunction(x,y):
return math.pow(x, y)
def anotherfunc():
return large_program()
You can also apply the with statement on multiple modules:
other_file.py
import math
import large_program
import even_larger_program
def afunction(x,y):
return math.pow(x, y)
def anotherfunc():
return large_program()
test_file.py
import unittest.mock as mock
import nothing
with mock.patch.dict('sys.modules', large_program=nothing), mock.patch.dict('sys.modules', even_larger_program=nothing):
from other_file import afunction
def this_test():
assert afunction(2, 2) == 4
I have a Python project that relies on a particular module, receivers.py, being imported.
I want to write a test to make sure it is imported, but I also want to write other tests for the behaviour of the code within the module.
The trouble is, that if I have any tests anywhere in my test suite that import or patch anything from receivers.py then it will automatically import the module, potentially making the test for import pass wrongly.
Any ideas?
(Note: specifically this is a Django project.)
One (somewhat imperfect) way of doing it is to use the following TestCase:
from django.test import TestCase
class ReceiverConnectionTestCase(TestCase):
"""TestCase that allows asserting that a given receiver is connected
to a signal.
Important: this will work correctly providing you:
1. Do not import or patch anything in the module containing the receiver
in any django.test.TestCase.
2. Do not import (except in the context of a method) the module
containing the receiver in any test module.
This is because as soon as you import/patch, the receiver will be connected
by your test and will be connected for the entire test suite run.
If you want to test the behaviour of the receiver, you may do this
providing it is a unittest.TestCase, and there is no import from the
receiver module in that test module.
Usage:
# myapp/receivers.py
from django.dispatch import receiver
from apples.signals import apple_eaten
from apples.models import Apple
#receiver(apple_eaten, sender=Apple)
def my_receiver(sender, **kwargs):
pass
# tests/integration_tests.py
from apples.signals import apple_eaten
from apples.models import Apple
class TestMyReceiverConnection(ReceiverConnectionTestCase):
def test_connection(self):
self.assert_receiver_is_connected(
'myapp.receivers.my_receiver',
signal=apple_eaten, sender=Apple)
"""
def assert_receiver_is_connected(self, receiver_string, signal, sender):
receivers = signal._live_receivers(sender)
receiver_strings = [
"{}.{}".format(r.__module__, r.__name__) for r in receivers]
if receiver_string not in receiver_strings:
raise AssertionError(
'{} is not connected to signal.'.format(receiver_string))
This works because Django runs django.test.TestCases before unittest.TestCases.
I'm writing unit tests to validate my project functionalities. I need to replace some of the functions with mock function and I thought to use the Python mock library. The implementation I used doesn't seem to work properly though and I don't understand where I'm doing wrong. Here a simplified scenario:
root/connector.py
from ftp_utils.py import *
def main():
config = yaml.safe_load("vendor_sftp.yaml")
downloaded_files = []
downloaded_files = get_files(config)
for f in downloaded_files:
#do something
root/utils/ftp_utils.py
import os
import sys
import pysftp
def get_files(config):
sftp = pysftp.Connection(config['host'], username=config['username'])
sftp.chdir(config['remote_dir'])
down_files = sftp.listdir()
if down_files is not None:
for f in down_files:
sftp.get(f, os.path.join(config['local_dir'], f), preserve_mtime=True)
return down_files
root/tests/connector_tester.py
import unittest
import mock
import ftp_utils
import connector
def get_mock_files():
return ['digital_spend.csv', 'tv_spend.csv']
class ConnectorTester(unittest.TestCase)
#mock.patch('ftp_utils.get_files', side_effect=get_mock_files)
def test_main_process(self, get_mock_files_function):
# I want to use a mock version of the get_files function
connector.main()
When I debug my test I expect that the get_files function called inside the main of connector.py is the get_mock_files(), but instead is the ftp_utils.get_files(). What am I doing wrong here? What should I change in my code to properly call the get_mock_file() mock?
Thanks,
Alessio
I think there are several problems with your scenario:
connector.py cannot import from ftp_utils.py that way
nor can connector_tester.py
as a habit, it is better to have your testing files under the form test_xxx.py
to use unittest with patching, see this example
In general, try to provide working minimal examples so that it is easier for everyone to run your code.
I modified rather heavily your example to make it work, but basically, the problem is that you patch 'ftp_utils.get_files' while it is not the reference that is actually called inside connector.main() but probably rather 'connector.get_files'.
Here is the modified example's directory:
test_connector.py
ftp_utils.py
connector.py
test_connector.py:
import unittest
import sys
import mock
import connector
def get_mock_files(*args, **kwargs):
return ['digital_spend.csv', 'tv_spend.csv']
class ConnectorTester(unittest.TestCase):
def setUp(self):
self.patcher = mock.patch('connector.get_files', side_effect=get_mock_files)
self.patcher.start()
def test_main_process(self):
# I want to use a mock version of the get_files function
connector.main()
suite = unittest.TestLoader().loadTestsFromTestCase(ConnectorTester)
if __name__ == "__main__":
unittest.main()
NB: what is called when running connector.main() is 'connector.get_files'
connector.py:
from ftp_utils import *
def main():
config = None
downloaded_files = []
downloaded_files = get_files(config)
for f in downloaded_files:
print(f)
connector/ftp_utils.py unchanged.