Python - Mock class init that instantiates another class inside - python-3.x

I have the following python file board.py:
def __init__(self, language):
self.foo = Foo(language)
self.words = Aux(self.foo)
And I'm creating this test_file:
#classmethod
def setUpClass(cls):
super().setUpClass()
cls.board = Board('pt')
def test_total_time(self):
self.board.total_time(True)
#some assert
But I'm getting a FileNotFoundError because Aux.___init____() calls a self.foo.method() that opens a file and reads from it.
Is there a way to mock self.foo.method(), or the class Aux?

You will want to patch the module. If you give me the name of the test file and the class you are testing. I can finish this answer for you.
In the test file:
import unittest
def BoardTestCase(unittest.TestCase):
#classmethod
def setUpClass(cls):
super().setUpClass()
cls.aux_mock = unittest.mock.patch('file_undertest.Aux')
cls.board = Board('pt')
def test_total_time(self):
self.board.total_time(True)
#some assert
I would suggest using pytest instead of the standard library unittest. Your tests will be written as functions meaning you can reuse the Board class only when needed to. You can set more robust fixtures (Board class test cases) and the mocker extension is more intuitive if you spend the 15 minutes to wrap your head around it.

Related

Fixture not found pytest

Hey I got a simple test where the fixure is not found. I am writting in vsc and using windows cmd to run pytest.
def test_graph_add_node(test_graph):
E fixture 'test_graph' not found
> available fixtures: cache, capfd, capfdbinary, caplog, capsys, capsysbinary, doctest_namespace, monkeypatch, pytestconfig, record_property, record_testsuite_property, record_xml_attribute, recwarn, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory
> use 'pytest --fixtures [testpath]' for help on them.
This is the error I get, here is the test code:
import pytest
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'giddeon1.settings')
import django
django.setup()
from graphs.models import Graph, Node, Tag
#pytest.fixture
def test_graph():
graph = Graph.objects.get(pk='74921f18-ed5f-4759-9f0c-699a51af4307')
return graph
def test_graph():
new_graph = Graph()
assert new_graph
def test_graph_add_node(test_graph):
assert test_graph.name == 'Test1'
im using python 3.9.2, pytest 6.2.5.
I have see some similar questions but they all handle wider or bigger problems.
You appear to be defining test_graph twice, which means that the second definition will overwrite the first. And you added #pytest.fixture to a test_ method when you used it, but #pytest.fixture should be added to non test methods so that tests can use that fixture. Here's how the code should probably look:
import pytest
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'giddeon1.settings')
import django
django.setup()
from graphs.models import Graph, Node, Tag
#pytest.fixture
def graph():
graph = Graph.objects.get(pk='74921f18-ed5f-4759-9f0c-699a51af4307')
return graph
def test_graph():
new_graph = Graph()
assert new_graph
def test_graph_add_node(graph):
assert graph.name == 'Test1'
Above, the first method has been renamed to graph so that the next method doesn't override it (and now #pytest.fixture is applied to a non-test method). Then, the 3rd method uses the graph fixture. Make any other changes as needed.

Calling class level variables using "#pytest.mark.parametrize" fixture

I'm trying to iterate a pytest test over a list of values which i generate inside another method in the test class.
The problem is that i get:
"#pytest.mark.parametrize("number",TestScratch.list_testing)
NameError: name 'TestScratch' is not defined" error when i try to run.I know for a fact that when i pass the list as a hard coded list i.e [0,3,54,90] it works.
Below is my code :
class TestScratch(object):
#classmethod
def setup_class(cls):
cls.list_testing = []
#classmethod
def setup_method(cls):
pass
def test_populate_list(self):
for i in range(100):
self.list_testing.append(i)
#pytest.mark.parametrize("number",TestScratch.list_testing)
def test_pytest_param(self, number):
assert type(number) == int
#classmethod
def teardown_class(cls):
'''
pass
'''
I also tried self.class.list_testing
but i get the same error
Environment details:
Python: 3.6.8
Pytest: 5.2.1
You can't use the class in the class definition. As the decorator is read at import time, e. g. while loading the class definition, and not at runtime, the class is not known at this point. You have to define the list outside the class instead:
import pytest
def populate_list():
test_list = []
for i in range(100):
test_list.append(i)
return test_list
list_testing = populate_list()
class TestScratch:
def test_populate_list(self):
# this will fail if list_testing could not be populated
assert len(list_testing) > 50
#pytest.mark.parametrize("number", list_testing)
def test_pytest_param(self, number):
# this will be skipped if list_testing could not be populated
assert type(number) == int
Any parameter you use in a decorator is read only once at load time, so trying to intialize it at runtime won't work. For example here you can find an explanation how parametrization works and why it is not possible to add parameters at runtime.

Pytest object created by object assert_called_once_with

I known how I can test if an injected object was called with a specific argument. But in my case the injected object will create an object that object will create another object and I want to test if that last object was called with the right argument.
in the example below the question would be if c.dirve was called with 100 as argument:
class car:
def drive(self, distance):
print("so fast")
class car_shop:
def buy_car(self):
return car()
class shop_shop:
def buy_shop(self):
return car_shop()
class processor:
def __init__(self, sshop):
self.sshop = sshop
def run(self):
cshop = self.sshop.buy_shop()
c = cshop.buy_car()
c.drive(100)
def main():
sshop = shop_shop()
proc = processor(sshop)
proc.run()
if __name__ == "__main__":
main()
is there a way to test that?
Since this was requested here my approach for testing these objects:
import pytest
from unittest.mock import Mock
from object_returns_object_test_for_arguments import processor, shop_shop
#pytest.fixture
def mock_shop_shop():
return Mock(spec=shop_shop)
def test_processor_car_called_with_100(mock_shop_shop):
proc = processor(mock_shop_shop)
proc.run()
assert mock_shop_shop.car_shop.car.drive.assert_called_once_with(100)
assert mock_shop_shop.car_shop.car.drive.call_count == 1
If using just the code shown in the question, you only have to mock car.drive. This could be done for example this way:
from unittest import mock
from object_returns_object_test_for_arguments import processor, shop_shop
#mock.patch('object_returns_object_test_for_arguments.car.drive')
def test_processor_car_called_with_100(drive_mock):
proc = processor(shop_shop())
proc.run()
drive_mock.assert_called_once_with(100)
As I don't know your real code, you may have to mock more stuff.
As an aside: class names in Python are written upper-case, camelcase-style by default.

Mocking a class in a Flask API

I have three files
helper.py
class helper:
def __init__(self, out_file):
self.out_file = out_file
def foo(first, second):
# Write data to file
flask_API.py
from helper import helper
#app.route('/', methods=['POST'])
def parse_request():
content = request.get_json()
out_file = #based on timestamp
helper(out_file).foo(content['first'], content['second'])
test_flask.py
import unittest
from unittest.mock import patch
import flask_API
class testFlaskAPI(unittest.TestCase):
def setUp(self):
self.app = flask_API.app.test_client()
self.app.test = True
#patch('flask_API.app.helper', return_value=None)
def test_service(self, mock_helper):
response = self.app.post(base_url, data=json.dumps({"some":"value"}, content_type='application/json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
I am having trouble mocking the helper class. This gives me an error saying
AttributeError: <Flask 'flask_API'> does not have the attribute 'helper'
I read that a class/method needs to be mocked where it is being called instead of where it's defined. Is there something wrong with the way I am patching the class?
In the end the solution turned out to be fairly simple. First there was no need to add app in the #patch decorator. The test just needed #patch('flask_API.helper'). Second, I first needed to return the mock of the class and then mock the function call as well. So the final answer turned out to be
#patch('flask_API.helper')
def test_service(self, mock_helper):
mocking_helper = mock_helper.return_value # mocking the class
mocking_helper.foo.return_value = None
response = self.app.post(base_url, data=json.dumps({"some":"value"}, content_type='application/json')
self.assertEqual(response.status_code, status.HTTP_200_OK)

Python unittest: Mock function in TestCase class

My approach to mock testing functions looks like this:
from unittest import mock, TestCase
from main import my_function
def my_mock(s):
if s == 'hello':
return 'goodbye'
return my_function(s)
class TestMyFunction(TestCase):
#mock.patch('my_function', side_effect=MyMock.my_mock)
def test_my_function(self, mock_get):
s = 'hello'
self.assertEqual('goodbye', my_function(s))
This works. But if I have multiple tests, where my_mock_1 patches test_my_function_1 and my_mock_2 patches test_my_function_2 and so on, the mock definitions are very far from the test definitions and the code becomes hard to read.
Is there a way to get the mock definition closer to the tests they belong to?
What I tried was
class TestMyFunction(TestCase):
#staticmethod
def my_mock_1(s):
...
#mock.patch('my_function', side_effect=my_mock_1)
def test_my_function_1(self, mock_get):
...
#staticmethod
def my_mock_2(s):
...
#mock.patch('my_function', side_effect=my_mock_2)
def test_my_function_2(self, mock_get):
...
...
But this fails with the exception
TypeError: 'staticmethod' object is not an iterator.

Resources