function in another file wont get mocked - python

I have this class in a file called local.py:
def get_credentials(creds):
data = creds
return data
class ClassA:
def __init__(self):
body = "not a json"
self.credentials = get_credentials(body)
def run(self):
print(self.credentials)
def main():
inst = ClassA()
inst.run()
if __name__ == "__main__":
main()
all it does is return the credentials passed to it.
I want to mock the get_credentials function for which I have another file test_local.py:
from local import ClassA
import unittest
from unittest.mock import patch
def mock_get_credentials(creds):
return "123"
class NameTestCase(unittest.TestCase):
patch('local.get_credessntials', new=mock_get_credentials("test"))
def test_whatever(self):
print("true")
inst = ClassA()
inst.run()
self.assertEqual(1, 1)
if __name__ == '__main__':
unittest.main()
The output I keep getting is this:
true
not a json
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
The fact that it spits out "not a json" to me shows that it is not taking in the mocked value. I don't understand why it's doing this as I feel I have follow the documentation. Would appreciate some help with this as to why its not getting mocked.

There are a few typos in your code: you forgot the # for the patch decorator, the patched name is wrong, and you pass the function result instead of the function to new. Here is the relevant part of the fixed test:
class NameTestCase(unittest.TestCase):
#patch('local.get_credentials', new=mock_get_credentials)
def test_whatever(self):
inst = ClassA()
inst.run()
self.assertEqual(1, 1)

Related

Python - unittest, mock, patch, input

so i've got a problem with my code.
File 1:
class Abc(object):
...
def function1(self):
#do something
def function2(self):
x = input()
return x+1
and now i'm trying to test function 2 so i wrote a test for it and i don't know what i am doing wrong:
from unittest.mock import patch
import unittest
from file1 import *
class TestBackend(unittest.TestCase):
def test_mode_first(self):
self.assertEqual(Abc().funcion1(), 30)
#patch('funcion2.input', create=True)
def test_mode_second(self, mocked_input):
mocked_input.side_effect = ["QWE"]
result = Abc().funcion2()
self.assertEqual(result, 10)
if __name__ == '__main__':
unittest.main()
i get ModuleNotFoundError: No module named 'function2'
so what i am doing wrong in here?
thanks for your help :)
You get ModuleNotFoundError because funcion2 is not a module. patch doc is clear about this:
target should be a string in the form 'package.module.ClassName'. The
target is imported and the specified object replaced with the new
object, so the target must be importable from the environment you are
calling patch() from. The target is imported when the decorated
function is executed, not at decoration time.
This works for me when executed with python3 -m unittest discover from the directory the files are in.
BTW you have a couple of typos in your example, e.g. Abc().funcion2(), note the missing t in funcion2.
Also, try not to use from … import *: https://docs.quantifiedcode.com/python-anti-patterns/maintainability/from_module_import_all_used.html#using-wildcard-imports-from-import
# file1.py
class Abc(object):
def function1(self):
return 30
def function2(self):
x = input()
return x + "1"
# test_file1.py
import unittest
from unittest.mock import patch
from file1 import Abc
class TestBackend(unittest.TestCase):
def test_mode_first(self):
self.assertEqual(Abc().function1(), 30)
#patch('builtins.input')
def test_mode_second(self, mocked_input):
mocked_input.return_value = "QWE"
result = Abc().function2()
self.assertEqual(result, "QWE1")

Python mock object instantiation

Using Python 2.7, and mock library
How can I test that certain patched object has been initialized with some specific arguments using mock?
Here some sample code and pseudo-code:
unittest.py :
import mock
#mock.patch('mylib.SomeObject')
def test_mytest(self, mock_someobject):
test1 = mock_someobject.return_value
test1 = method_inside_someobject.side_effect = ['something']
mylib.method_to_test()
# How can I assert that method_to_test instanced SomeObject with certain arguments?
# I further test things with that method_inside_someobject call, no problems there...
mylib.py :
from someobjectmodule import SomeObject
def method_to_test():
obj = SomeObject(arg1=val1, arg2=val2, arg3=val3)
obj.method_inside_someobject()
So, how can I test SomeObject was instanced with arg1=val1, arg2=val2, arg3=val3?
If you replaced a class with a mock, creating an instance is just another call. Assert that the right parameters have been passed to that call, for example, with mock.assert_called_with():
mock_someobject.assert_called_with(arg1=val1, arg2=val2, arg3=val3)
To illustrate, I've updated your MCVE to a working example:
test.py:
import mock
import unittest
import mylib
class TestMyLib(unittest.TestCase):
#mock.patch('mylib.SomeObject')
def test_mytest(self, mock_someobject):
mock_instance = mock_someobject.return_value
mock_instance.method_inside_someobject.side_effect = ['something']
retval = mylib.method_to_test()
mock_someobject.assert_called_with(arg1='foo', arg2='bar', arg3='baz')
self.assertEqual(retval, 'something')
if __name__ == '__main__':
unittest.main()
mylib.py:
from someobjectmodule import SomeObject
def method_to_test():
obj = SomeObject(arg1='foo', arg2='bar', arg3='baz')
return obj.method_inside_someobject()
someobjectmodule.py:
class SomeObject(object):
def method_inside_someobject(self):
return 'The real thing'
and running the test:
$ python test.py
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK

Using Python's UnitTest Mock for a new Object

I'm trying to learn how to use Mocks for Python. However I've been struggling with some basic application of it.
Let's say our piece of code that I want to test is this:
class ProductionClass:
def method(self):
newone=ProductionClass()
newone.something(1, 2, 3)
def something(self, a, b, c):
pass
def __init__(self):
print("Test")
Which have a method which is simply create a new object of itself and calls a method of that class.
import unittest
import unittest.mock
from ProductionClass import *
from unittest.mock import *
class TestProduction(unittest.TestCase):
def test_one(self):
real = ProductionClass()
real.something = MagicMock()
real.method()
real.something.assert_called_once_with(1, 2, 3)
if __name__ == '__main__':
unittest.main()
Once again, this is a very simple UnitTest, basically copypasted from 26.5.1.1 of https://docs.python.org/3/library/unittest.mock-examples.html .
However, this would test if real.something has been called, meanwhile the one i really want to test is if newone.something has been called.
Considering newone is created later when we actually call method()-method how do I use mock to test it?
You can test this by simply instantiate ProductionClass in setUp method and patch ProductionClass in test_one as follows
import unittest
import ProductionClass
import mock
class TestProduction(unittest.TestCase):
def setUp(self):
self.real = ProductionClass.ProductionClass()
#mock.patch("ProductionClass.ProductionClass")
def test_one(self, mock1):
print "From Test : %s " % mock1()
real = self.real
real.method()
mock1().something.assert_called_once_with(1, 2, 3)
if __name__ == '__main__':
unittest.main()
I just modified the production class to show that both object refers to the same instance of the mock
class ProductionClass:
def method(self):
newone=ProductionClass()
print "From Production class : %s" % newone
newone.something(1, 2, 3)
def something(self, a, b, c):
pass
def __init__(self):
print("Test")
Output:
Testing started at 5:52 PM ...
Test
From Test : <MagicMock name='ProductionClass()' id='4330372048'>
From Production class : <MagicMock name='ProductionClass()' id='4330372048'>
Process finished with exit code 0
You can verify that both object refers the same instance of the mock object by looking the id
PS: I've been using mock package for this example so you probably need to install it using pip.

is it possible to route errors to class-wrapped Bottle methods?

I wrap Bottle in a class and would like to bottle.route() error handlers. Is this possible? I currently use decorators on unbound functions as described in the docs (the comments reflect how I would like to change the code)
import bottle
class WebServer(object):
def __init__(self):
self.message = "hello world"
bottle.route("/", 'GET', self.root)
# bottle.route(error 404 to self.error404)
bottle.run(host='0.0.0.0', debug=True)
def root(self):
print("hello from root")
#bottle.error(404)
def error404(error):
# I would like to print self.message here after the routing above
print("error 404")
return
if __name__ == "__main__":
WebServer()
Note: I read the warning in another SO thread about not doing the routing in __init__ but I will be using only one instance of the class.
You should be able to define your error handler within Webserver.__init__:
import bottle
class WebServer(object):
def __init__(self):
self.message = "hello world"
bottle.route("/", 'GET', self.root)
# just define your error function here,
# while you still have a reference to "self"
#bottle.error(404)
def error404(error):
# do what you want with "self"
print(self.message)
return self.message
bottle.run(host='0.0.0.0', debug=True)
def root(self):
print("hello from root")
if __name__ == "__main__":
WebServer()

Batch tests (unittest) in Python

I have a unittest that tests the connection of an url. Individually it works, but I have several urls to test, so I'm trying to call this test module and batch-test them! But I get errors in calling the test function. Could you help me?
test.py:
class TestConnector(unittest.TestCase):
def setUp(self):
[...]
def test_connection(self, url):
conn = Connector(self.user)
self.assertNotEqual(conn.read(url), None)
if __name__ == '__main__':
unittest.main()
Now I want to test several urls, so I created file with them, and try to call the test function:
import test
with open('URL_list.txt') as f:
urls = f.readlines()
suite = unittest.TestLoader().loadTestsFromModule(test.TestConnector)
for url in urls:
unittest.TextTestRunner().run(suite)
And I get this message as many times as urls I have:
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
----------------------------------------------------------------------
What's wrong?
Your test method is ignored because it takes an argument. Test methods never take an argument. This is quite beside the fact that Python won't ever magically pass a local variable name into a function as an argument; you'd have to explicitly pass it in.
Integrate your url loading into the test itself instead:
class TestConnector(unittest.TestCase):
def setUp(self):
[...]
def test_connections(self):
with open('URL_list.txt') as f:
for url in f:
conn = Connector(self.user)
self.assertNotEqual(conn.read(url.strip()), None)
if __name__ == '__main__':
unittest.main()
or create test methods dynamically:
class TestConnector(unittest.TestCase):
def setUp(self):
[...]
def generate_test(url):
def test(self):
conn = Connector(self.user)
self.assertNotEqual(conn.read(url), None)
if __name__ == '__main__':
with open('URL_list.txt') as f:
for i, url in enumerate(f):
test_name = 'test_{}'.format(i)
setattr(TestConnector, test_name, generate_test(url.strip()))
unittest.main()

Categories

Resources