Mock two methods for the same function python - python

I want to mock two methods (predict_proba and classes_) of a sklearn model. I have a function that receives a template and text, and returns a label and a score.
import numpy as np
from unittest.mock import MagicMock
def model_predict_proba(model, text):
pred_proba_model = model.predict_proba([text])
score = pred_proba_model.max()
label = model.classes_[np.argmax(pred_proba_model)]
return label, score
def test_model_predict_proba():
mock_model = MagicMock()
mock_model.predict_proba.return_value = np.array([0.90, 0.23])
mock_model.classes_.return_value= np.array(['FOOD', 'DRINK'])
text = 'Apple pie'
expected = ("FOOD", 0.90)
result = model_predict_proba(mock_model, text)
assert result == expected
When I run this test, I get the following error message:
Can someone help me?

This should do the trick:
def model_predict_proba(model, text):
pred_proba_model = model.predict_proba([text])
score = pred_proba_model.max()
label = model.classes_[np.argmax(pred_proba_model)]
return label, score
def test_model_predict_proba():
mock_model = MagicMock()
mock_model.predict_proba.return_value.max.return_value = 0.90
mock_model.classes_.__getitem__.return_value ='FOOD'
text = 'Apple pie'
expected = ("FOOD", 0.90)
result = model_predict_proba(mock_model, text)
assert result == expected
Note that since you're mocking your model, this test is not actually testing the model in any useful way -- I'm assuming you're writing this function just as an exercise to understand how MagicMock works. The purpose of mocking is usually to simulate the inputs or dependencies of the thing you're testing, rather than the thing itself.

Related

How to mock a Python method that is called more than once inside a loop using Pytest

Let's say I have a Python method:
def good_method(self) -> None:
txt = "Some text"
response = self.bad_method(txt)
resources = response["resources"]
print (resources)
while resources:
response = self.bad_method(txt)
resources = response["resources"]
print (resources)
Now let's say I want to write a unit test for it. The bad_method() returns a dictionary and it could get called over and over in the while loop. I have been trying to Mock the bad_method() so it returns a nested dictionary, so the while loop runs once. This is the code:
from unittest.mock import MagicMock
def test_good_method():
dic = {"resources": {"resources": "values"}}
def side_effect():
return dic
self.bad_method() = MagicMock(side_effect=side_effect())
self.good_method()
I expected to first get a {"resources": "values"} printed out, and then a values. But the only thing I get is a resources. What am I doing wrong? How can I achieve what I expect?
def test_good_method():
thing = MyClassContainingGoodAndBadMethod()
thing.bad_method = MagicMock(return_value={"a": "b"})
thing.good_method()
assert thing.bad_method.call_count == 10 # or however many times it is supposed to be called

Not sure why MyMock.env["key1"].search.side_effect=["a", "b"] works but MyMock.env["key1"] = ["a"] with MyMock.env["key2"] = ["b"] does not work

I had created a simple example to illustrate my issue. First is the setup say mydummy.py:
class TstObj:
def __init__(self, name):
self.name = name
def search(self):
return self.name
MyData = {}
MyData["object1"] = TstObj("object1")
MyData["object2"] = TstObj("object2")
MyData["object3"] = TstObj("object3")
def getObject1Data():
return MyData["object1"].search()
def getObject2Data():
return MyData["object2"].search()
def getObject3Data():
return MyData["object3"].search()
def getExample():
res = f"{getObject1Data()}{getObject2Data()}{getObject3Data()}"
return res
Here is the test that failed.
def test_get_dummy1():
dummy.MyData = MagicMock()
mydummy.MyData["object1"].search.side_effect = ["obj1"]
mydummy.MyData["object2"].search.side_effect = ["obj2"]
mydummy.MyData["object3"].search.side_effect = ["obj3"]
assert mydummy.getExample() == "obj1obj2obj3"
The above failed with run time error:
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/unittest/mock.py:1078: StopIteration
Here is the test that passed:
def test_get_dummy2():
dummy.MyData = MagicMock()
mydummy.MyData["object1"].search.side_effect = ["obj1", "obj2", "obj3"]
assert mydummy.getExample() == "obj1obj2obj3"
Am I missing something? I would have expected test_get_dummy1() to work and test_get_dummy2() to fail and not vice versa. Where and how can I find/learn more information about mocking to explain what is going on...
MyData["object1"] is converted to this function call: MyData.__getitem__("object1"). When you call your getExample method, the __getitem__ method is called 3 times with 3 parameters ("object1", "object2", "object3").
To mock the behavior you could have written your test like so:
def test_get_dummy_alternative():
mydummy.MyData = MagicMock()
mydummy.MyData.__getitem__.return_value.search.side_effect = ["obj1", "obj2", "obj3"]
assert mydummy.getExample() == "obj1obj2obj3"
Note the small change from your version: mydummy.MyData["object1"]... became: mydummy.MyData.__getitem__.return_value.... This is the regular MagicMock syntax - we want to to change the return value of the __getitem__ method.
BONUS:
I often struggle with mock syntax and understanding what's happening under the hood. This is why I wrote a helper library: the pytest-mock-generator. It can show you the actual calls made to the mock object.
To use it in your case you could have added this "exploration test":
def test_get_dummy_explore(mg):
mydummy.MyData = MagicMock()
mydummy.getExample()
mg.generate_asserts(mydummy.MyData, name='mydummy.MyData')
When you execute this test, the following output is printed to the console, which contains all the asserts to the actual calls to the mock:
from mock import call
mydummy.MyData.__getitem__.assert_has_calls(calls=[call('object1'),call('object2'),call('object3'),])
mydummy.MyData.__getitem__.return_value.search.assert_has_calls(calls=[call(),call(),call(),])
mydummy.MyData.__getitem__.return_value.search.return_value.__str__.assert_has_calls(calls=[call(),call(),call(),])
You can easily derive from here what has to be mocked.

How can I write automatic tests for this Python code?

My script core.py which is found in the folder preprocessing takes a string and cleans it. It is part of a bigger model (see the last import, but it's unimportant). The dict_english, found in app/core/preprocessing/constants, is just a dictionary of uncommon English words that I replace with other words.
import string
from app.core.preprocessing.constants import dict_english
from app.core.generic.step import Step
from typing import Optional
from app.api.model.my_project_parameters import MyProjectParameters
class TextPreprocessingBase(Step[str, str]):
def process(self, input_value: str, parameters: Optional[MyProjectParameters] = None) -> str:
input_value = input_value.replace("'", '')
input_value = input_value.replace("\"", '')
printable = set(string.printable)
filter(lambda x: x in printable, input_value)
new_string=''.join(filter(lambda x: x in printable, input_value))
return new_string
class TextPreprocessingEnglish(TextPreprocessingBase):
def process(self, input_value: str, parameters: Optional[MyProjectParameters] = None) -> str:
process_english = super().process(input_value, parameters)
for word, initial in dict_english.items():
process_english = process_english.replace(word.lower(), initial)
return process_english
It's easy to test:
string_example= """ Random 'text' ✓"""
a = TextPreprocessingEnglish()
output = a.process(string_example)
print(output)
It prints:
Random text
But I want to write some automatic tests. I thought:
import pytest
from app.core.preprocessing.core import TextPreprocessingBase, TextPreprocessingEnglish
class TestEnglishPreprocessing:
#pytest.fixture(scope='class')
def english_preprocessing:
...
But I'm stuck here. I just want to test my code on several various strings that I manually write. Is it possible to do this like that or do I just write it like the simple test example above?
This sounds like something you could solve by parametrizing a test, for example:
import pytest
from process import TextPreprocessingEnglish
#pytest.mark.parametrize(
"input,expected",
[
(""" Random 'text' ✓""", "Random text"),
(""" Some other 'text' ✓""", "Some other text"),
],
)
def test_process(input, expected):
a = TextPreprocessingEnglish()
output = a.process(input)
assert output == expected

How to write pytest for boto3 lambda invoke when it is defined inside a function

I am trying to write pytest to test the following method by mocking the boto3 client. I tried with sample test case. I am not sure if that is right way to do it. Please correct me if I am wrong.
//temp.py
import boto3
import json
def temp_lambda(event):
client_lam = boto3.client('lambda', region_name="eu-west-1") #defined inside the function.
obj = client_lam.invoke(
FunctionName='XYZ',
InvocationType='ABC',
Payload=json.dumps({'payload': event}))
return obj
//test_temp.py
import mock
from unittest.mock import MagicMock, patch
from .temp import temp_lambda
#mock.patch("boto3.client")
def test_temp_lambda(mock_lambda_client):
mocked_response = MagicMock(return_value = 'yes')
mock_lambda_client.invoke.return_value = mocked_response.return_value
event = {}
x = temp_lambda(event)
assert x == 'yes'
I am getting assertion error in output
AssertionError: assert <MagicMock name='client().invoke()' id='2557742644480'> == 'yes'
def test_temp_lambda(context):
with patch('boto3.client') as mocked_response:
mocked= MagicMock()
mocked.invoke.return_value= "ok"
mocked_response.return_value=mocked
event = {}
x = temp_lambda(event)
assert response=='ok'
The golden rule of the mock framework is that you mock where the object is used not where it's defined.
So it would be #mock.patch('temp.boto3.client')

Getting Python's nosetests results in a tearDown() method

I want to be able to get the result of a particular test method and output it inside the teardown method, while using the nose test runner.
There is a very good example here.
But unfortunately, running nosetests example.py does not work, since nose doesn't seem to like the fact that the run method in the superclass is being overridden:
AttributeError: 'ResultProxy' object has no attribute 'wasSuccessful'
Caveat: the following doesn't actually access the test during the tearDown, but it does access each result.
You might want to write a nose plugin (see the API documentation here). The method that you are probably interested in is afterTest(), which is run... after the test. :) Though, depending on your exact application, handleError()/handleFailure() or finalize() might actually be more useful.
Here is an example plugin that accesses the result of a test immediately after it is executed.
from nose.plugins import Plugin
import logging
log = logging.getLogger('nose.plugins.testnamer')
class ReportResults(Plugin):
def __init__(self, *args, **kwargs):
super(ReportResults, self).__init__(*args, **kwargs)
self.passes = 0
self.failures = 0
def afterTest(self, test):
if test.passed:
self.passes += 1
else:
self.failures += 1
def finalize(self, result):
print "%d successes, %d failures" % (self.passes, self.failures)
This trivial example merely reports the number of passes and failures (like the link you included, but I'm sure you can extend it to do something more interesting (here's another fun idea). To use this, make sure that it is installed in Nose (or load it into a custom runner), and then activate it with --with-reportresults.
If you are OK with adding some boilerplate code to the tests, something like the following might work.
In MyTest1, tearDown is called at the end of each test, and the value of self.result has been set to a tuple containing the method name and a dictionary (but you could set that to whatever you like). The inspect module is used to get the method name, so tearDown knows which test just ran.
In MyTest2, all the results are saved in a dictionary (results), which you can do with what you like in the tearDownClass method.
import inspect
import unittest
class MyTest1(unittest.TestCase):
result = None
def tearDown(self):
print "tearDown:", self.result
def test_aaa(self):
frame = inspect.currentframe()
name = inspect.getframeinfo(frame).function
del frame
self.result = (name, None)
x = 1 + 1
self.assertEqual(x, 2)
self.result = (name, dict(x=x))
def test_bbb(self):
frame = inspect.currentframe()
name = inspect.getframeinfo(frame).function
del frame
self.result = (name, None)
# Intentional fail.
x = -1
self.assertEqual(x, 0)
self.result = (name, dict(x=x))
class MyTest2(unittest.TestCase):
results = {}
#classmethod
def tearDownClass(cls):
print "tearDownClass:", cls.results
def test_aaa(self):
frame = inspect.currentframe()
name = inspect.getframeinfo(frame).function
del frame
self.results[name] = None
x = 1 + 1
self.assertEqual(x, 2)
self.results[name] = dict(x=x)
def test_bbb(self):
frame = inspect.currentframe()
name = inspect.getframeinfo(frame).function
del frame
self.results[name] = None
x = -1
self.assertEqual(x, 0)
self.results[name] = dict(x=x)
if __name__ == '__main__':
unittest.main()

Categories