unittest mock.patch only once - python

I am testing a function doing two times a "requests.post". Si I stub the call to this function. However, I want that the first stub return a fake data, and the second stub another fake data. How to do it ?
Currently, I have :
#mock.patch('requests.post', side_effect=mocked_object)
def test_function_ok(self, mock_post):
...
And I want something like this :
#mock.patch_once('requests.post', side_effect=mocked_1)
#mock.patch_once('requests.post', side_effect=mocked_2)
def test_function_ok(self, mock_post):
...

Thanks to MrBean Bremen. However, I must call the elements in the list, like this :
#mock.patch('requests.post', side_effect=[mocked_1(), mocked_2()])
def test_function_ok(self, mock_post):
...

You can send different data to be mocked to the request twice it will mock the method and return the expected data that is specified in return_value
#patch('the_function_path_you_want_to_mock')
def test_data(self, mock_obj):
mock_obj.return_value.function_to_be_mocked.return_value = [data_to_mocked]
response = self.client.post('/request', data=json.dumps(self.input_data), content_type='application/vnd.api+json', )
mock_obj.return_value.function_to_be_mocked.return_value = [data_to_mocked]
response = self.client.post('/request', data=json.dumps(self.input_data), content_type='application/vnd.api+json', )

Related

Better way to Test API?

I have function :
def is_car_exist(make, model):
url = f'https://vpic.nhtsa.dot.gov/api/vehicles/GetModelsForMake/\
{make.capitalize()}?format=json'
data = requests.get(url).json()['Results']
return any(model.capitalize() == car['Model_Name'] for car in data)
How write test to test requests method inside function ?

Mocking return value of a nested call in Python mock library

Brand new to this library
Here is the call stack of my mocked object
[call(),
call('test'),
call().instance('test'),
call().instance().database('test'),
call().instance().database().snapshot(),
call().instance().database().snapshot().__enter__(),
call().instance().database().snapshot().__enter__().execute_sql('SELECT * FROM users'),
call().instance().database().snapshot().__exit__(None, None, None),
call().instance().database().snapshot().__enter__().execute_sql().__iter__()]
Here is the code I have used
#mock.patch('testmodule.Client')
def test_read_with_query(self, mock_client):
mock = mock_client()
pipeline = TestPipeline()
records = pipeline | ReadFromSpanner(TEST_PROJECT_ID, TEST_INSTANCE_ID, self.database_id).with_query('SELECT * FROM users')
pipeline.run()
print mock_client.mock_calls
exit()
I want to mock this whole stack that eventually it gives me some fake data which I will provide as a return value.
The code being tested is
spanner_client = Client(self.project_id)
instance = spanner_client.instance(self.instance_id)
database = instance.database(self.database_id)
with database.snapshot() as snapshot:
results = snapshot.execute_sql(self.query)
So my requirements is that the results variable should contain the data I will provide.
How can I provide a return value to such a nested calls
Thanks
Create separate MagicMock instances for the instance, database and snapshot objects in the code under test. Use return_value to configure the return values of each method. Here is an example. I simplified the method under test to just be a free standing function called mut.
# test_module.py : the module under test
class Client:
pass
def mut(project_id, instance_id, database_id, query):
spanner_client = Client(project_id)
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)
with database.snapshot() as snapshot:
results = snapshot.execute_sql(query)
return results
# test code (pytest)
from unittest.mock import MagicMock
from unittest import mock
from test_module import mut
#mock.patch('test_module.Client')
def test_read_with_query(mock_client_class):
mock_client = MagicMock()
mock_instance = MagicMock()
mock_database = MagicMock()
mock_snapshot = MagicMock()
expected = 'fake query results'
mock_client_class.return_value = mock_client
mock_client.instance.return_value = mock_instance
mock_instance.database.return_value = mock_database
mock_database.snapshot.return_value = mock_snapshot
mock_snapshot.execute_sql.return_value = expected
mock_snapshot.__enter__.return_value = mock_snapshot
observed = mut(29, 42, 77, 'select *')
mock_client_class.assert_called_once_with(29)
mock_client.instance.assert_called_once_with(42)
mock_instance.database.assert_called_once_with(77)
mock_database.snapshot.assert_called_once_with()
mock_snapshot.__enter__.assert_called_once_with()
mock_snapshot.execute_sql.assert_called_once_with('select *')
assert observed == expected
This test is kind of portly. Consider breaking it apart by using a fixture and a before function that sets up the mocks.
Either set the value directly to your Mock instance (those enters and exit should have not seen) with:
mock.return_value.instance.return_value.database.return_value.snapshot.return_value.execute_sql.return_value = MY_MOCKED_DATA
or patch and set return_value to target method, something like:
#mock.patch('database_engine.execute_sql', return_value=MY_MOCKED_DATA)

Django project show processed data instantly

I have one django project. It has one function in view.py to process the data from the inputs to give the output for other function. However the processing time for the function is kind of long. I want to fulfill the instant demonstration of the processed output. How could I achieved that? The following processing() function is for the processing purpose. And the output 'user_entries' is for the demonstration in results() as followed.
def processing(request):
import sys
n = []
for topic in Topic.objects.filter(owner=request.user).order_by("date_added"):
entries = topic.entries.all()
m = []
for p in entries:
q = p.text
m.append(q)
n.append(m)
list = []
start(list, n)
request.session['user_entries'] = list
return request.session['user_entries']
def results(request):
data = processing(request)
return render(request, "project/results.html", {"datas": data})
In the start() function of the processing() function. There is one part list.append() to add new output into list. But it seems that the new appended list cannot be transferred and show the instant results in project/results.html?
What you're doing could likely be done a lot more simply.
def results(request):
return render(
request,
"project/results.html",
{
"user_entries": Entry.objects.filter(topic__owner=request.user),
"start_values": "...", # Whatever start is appending...
},
)
Since you have a foreign key from entry to User, you could also use request.user.topic_set.all() to get the current user's topics.
Or, if you actually do need those lists nested...
# ...
"user_entries": (
topic.entries.all() for topic in
Topic.objects.filter(owner=request.user)
),
# ...
Just based on what you're showing us, it seems like your ordering -- for both Topic and Entry -- should probably have a sensible default set in, e.g., Topic.Meta.ordering, which in this case would probably look like this:
class Topic(models.Model):
# ...
class Meta:
ordering = ("date_added",)
# ...
That way, in this and most other cases, you would not have to apply .ordering(...) manually.

django - combine the output of json views

I write a simple json api, I use one base class, and I mostly write one api view per one model class. What I want is to combine the output of few views into one url endpoint, with as least as possible additional code.
code:
# base class
class JsonView(View):
def get(self, request):
return JsonResponse(self.get_json())
def get_json(self):
return {}
class DerivedView(JsonView):
param = None
def get_json(self):
# .. use param..
return {'data': []}
urls.py:
url('/endpoint1', DerivedView.as_view(param=1))
url('/endpoint2', DerivedView2.as_view())
# What I want:
url('/combined', combine_json_views({
'output1': DerivedView.as_view(param=1),
'output2': DerivedView2.as_view()
}))
So /combined would give me the following json response:
{'output1': {'data': []}, 'output2': output of DerivedView2}
This is how combine_json_views could be implemented:
def combine_json_views(views_dict):
d = {}
for key, view in views_dict.items():
d[key] = view() # The problem is here
return json.dumps(d)
The problem is that calling view() give me the encoded json, so calling json.dumps again gives invalid json. I could call json.loads(view()), but that looks bad to decode the json that I just encoded.
How can I modify the code (maybe a better base class) here, while keeping it elegant and short? without adding too much code. Is there any way to access the data (dict) which is used to construct JsonResponse?
You can create a combined view that calls the get_json() methods and combines them:
class CombinedView(JsonView):
def get_json(self):
view1 = DerivedView(param=1)
view2 = DerivedView2()
d = view1.get_json()
d.update(view2.get_json())
return d
then:
url('/combined', CombinedView.as_view()),

Mock in python 'open' of two files

I want to test appendRole which is called getFileAsJson to read file with open.
My problem is that I don't know which open will be next. There are many if/elif.
def appendRole(self, hosts=None, _newrole=None, newSubroles=None, undoRole=False, config_path=None):
""" Same as changeRole but keeps subroles """
if hosts is None:
hosts = ["127.0.0.1"]
if newSubroles is None:
newSubroles = {}
if config_path is None:
config_path = self.config_path
with self._lock:
default = {}
data = self.getFileAsJson(config_path, default)
...................
...................
...................
...................
data1 = self.getFileAsJson(self.config_path_all, {"some"})
data2 = self.getFileAsJson(self.config_path_core, {"something"})
...................
...................
...................
def getFileAsJson(self, config_path, init_value):
"""
read file and return json data
if it wasn't create. Will created.
"""
self.createFile(config_path, init_value)
try:
with open(config_path, "r") as json_data:
data = json.load(json_data)
return data
except Exception as e:
self.logAndRaiseValueError(
"Can't read data from %s because %s" % (config_path, e))
Even you can find an answer to your question at Python mock builtin 'open' in a class using two different files I would like encourage you to change your approach to write tests for getFileAsJson() and then trust it.
To test appendRole() use mock.patch to patch getFileAsJson(), then by side_effect attribute you can instruct the mock to return exactly what you need for your test.
So, after some test on getFileAsJson() where you can use mock_open() to mock open builtin (maybe you need to patch createFile() too). Your appendRole()'s test looks like something like this:
#mock.patch('mymodule.getFileAsJson', autospec=True)
def test_appendRole(self, mock_getFileAsJson)
mock_getFileAsJson.side_effect = [m_data, m_data1,m_data2,...]
# where m_data, m_data1,m_data2, ... is what is supposed
# getFileAsJson return in your test
# Invoke appendRole() to test it
appendRole(bla, bla)
# Now you can use mock_getFileAsJson.assert* family methods to
# check how your appendRole call it.
# Moreover add what you need to test in appendRole()

Categories