Say I have a class called Client that creates an object of the Request class and passes it to a method of a Connection object:
class Client(object):
def __init__(self, connection):
self._conn = connection
def sendText(plaintext):
self._conn.send(Request(0, plaintext))
And I want to assert the object passed into the Connection.send method to check its properties. I start with creating a mocked Connection class:
conn = Mock()
client = Client(conn)
client.sendText('some message')
And then I want something like:
conn.send.assert_called_with(
(Request,
{'type': 0, 'text': 'some message'})
)
Where 'type' and 'text' are properties of Request. Is there a way to do this in python's mock? All I found in the documentation were simple data examples.
I could have done it with mock.patch decorator by replacing the original 'send' method with a method which asserts the object's fields:
def patchedSend(self, req):
assert req.Type == 0
with mock.patch.object(Connection, 'send', TestClient.patchedSend):
...
but in this case I would have to define a separete mocked function for every method check and I couldn't check (without additional coding) if the function has been called at all.
You can get the last arguments to the mock with
request, = conn.send.call_args
and then assert properties about that. If you want facilities to express more sophisticated assertions about things, you can install PyHamcrest.
Note: Don't use assert in unit tests. Use assertion methods like assertEqual or assertTrue. Assertion methods can't be accidentally turned off, and they can give more useful messages than assert statements.
Well, I think that the easiest and better way of doing it, in this specific case, is to make a function to create requests and then mock it.
For instance, it'd be something like it:
class Client(object):
def __init__(self, connection):
self._conn = connection
def _create_request(self, plain_text):
return Request(0, plain_text)
def send_text(self, plaintext):
self._conn.send(self._create_request(plain_text))
And then, in the test, you could mock _create_request to return some specific value and then assert that send_text was called with it.
You could also get the parameters by call_args, as suggested, but I think it looks better this way.
Related
Hello I am pretty new to unit testing and therefore I got a big issue with unittest.mock.
My Project consists of different modules.
My first module is General:
general.py
def do_something(item, collection):
# some more code that i want to test
try:
collection.insert_one(item)
except:
print("did not work")
My second module ist my_module.py
mymodule.py
import general
from pymongo import MongoClient
client = MongoClient("localhost", 27017)
db = client['db']
collection = db['col']
item =
{
"id": 1
}
def method:
general.do_something(item, collection)
Now I want to test the do_something(item, collection) method from general.py, and therefore I want to mock the collection.insert_one(item). I did not find a possible solution for this.
I tried it with patch, but my Problem is, that the parameter collection (which is a pymongo Collection) is a parameter that calls a function. How can i now manage to mock collection.insert_one?
My target is to extract collection.insert_one and set a MagicMock into it. And this Magic Mock should have the possibility to crash to check if the except part works or to not crash to check if the try part workes.
TestGeneral.py
import unnittest
class TestGeneral(unittest.TestCase):
#patch()
def test_general():
Thank you in advance! :)
You don't actually need a mock here, you could just create a class that has a similar functionality.
TestGeneral.py
import unnittest
class TestGeneral(unittest.TestCase):
def test_general_success():
assert general.do_something(collection(), None)
def test_general_failure():
with self.assertRaises(Exception):
general.do_something(collection(fail=True), None))
class collection:
def __init__(self, fail=False):
self.fail = fail
def insert_one(self, item):
if self.fail:
raise Exception
return True
Then you can also check the stdout to make sure a print is used on success
If you had to use mock for some reason, this method could work
TestGeneral.py
import unnittest
class TestGeneral(unittest.TestCase):
#mock.patch('{your path}.my_module.MongoClient')
def test_general_success(mock_mongo):
mock_mongo.return_value = {'db': None, 'col': collection()}
assert general.do_something(None, None) # None here since the collection is mocked anyway
#mock.patch('{your path}.my_module.MongoClient')
def test_general_failure(mock_mongo):
mock_mongo.return_value = {'db': None, 'col': collection(fail=True)}
with self.assertRaises(Exception):
general.do_something(None, None))
class collection:
def __init__(self, fail=False):
self.fail = fail
def insert_one(self, item):
if self.fail:
raise Exception
return True
The downside to this is that you have now mocked the entire MongoDB, meaning any other uses of it (i.e client) will also be mocked
I am looking for a magic method similar to the __get__ method, but in my case the variable is not inside another class, I want something like:
class A(object):
def __similar_to_get__(self):
print 'called'
return self
a = A()
b = a
>>> 'called'
Is it possible?
The reason I am asking this is, I am using a python mock library, let's say a function I am testing uses URI attribute, and I want to mock it to return different values in subsequent calls. Eg:
class WebService(obj):
URI = 'http://works.com'
def dowork(self):
call_api(self.URI)
For me to mock a failure I am using the mock library:
mock = MagicMock()
mock.side_effect = ['http://fail.com', 'http://works.com']
with patch('WebService.URI', mock):
# do the testing
But the problem is I can only get mock to return the urls by calling the callable mock() not just simply accessing mock
PS: I am a mock noob.
Not directly answering my own question, I managed to use a property to work around this:
from dps.px.pxpost import PxPost
mock = MagicMock()
mock.side_effect = ['http://doesnotexist', PxPost.URI]
def URI(self):
return mock()
with patch('django.conf.settings.CELERY_ALWAYS_EAGER', True, create=True):
with patch('dps.px.pxpost.PxPost.URI', property(URI)):
self.transaction.process()
for sub_transaction in self.transaction.sub_transactions.all():
self.assertTrue(isinstance(sub_transaction.state, CompletedSubTransaction))
self.assertTrue(sub_transaction.transaction_logs.count() > 0)
self.assertTrue(isinstance(self.transaction.state, CompletedTransaction))
I've a class Client which has many methods:
class Client:
def compute(self, arg):
#code
#more methods
All the methods of this class runs synchronously. I want to run them asynchronously. There are too many ways to accomplish this. But I'm thinking along these lines:
AsyncClient = make_async(Client) #make all methods of Client async, etc!
client = AsyncClient() #create an instance of AsyncClient
client.async_compute(arg) #compute asynchronously
client.compute(arg) #synchronous method should still exist!
Alright, that looks too ambitious, and I feel it can be done.
So far I've written this:
def make_async(cls):
class async_cls(cls): #derive from the given class
def __getattr__(self, attr):
for i in dir(cls):
if ("async_" + i) == attr:
#THE PROBLEM IS HERE
#how to get the method with name <i>?
return cls.__getattr__(i) # DOES NOT WORK
return async_cls
As you see the comment in the code above, the problem is to get the method given its name as string. How to do that? Once I get the method, I would wrap it in async_caller method, etc — the rest of the work I hope I can do myself.
The function __getattr__ just works with class instance, not class. Use getattr(cls, method_name) instead, it will solve the problem.
getattr(cls, method_name)
Say I have the following test class:
# file tests.py
class MyTests(object):
nose_use_this = True
def test_something(self):
assert 1
I can easily write a plugin that is run before that test:
class HelloWorld(Plugin):
# snip
def startTest(self, test):
import ipdb; ipdb.set_trace()
The test is what I want it to be, but the type of test is nose.case.Test:
ipdb> str(test)
'tests.MyTests.test_something'
ipdb> type(test)
<class 'nose.case.Test'>
And I can't see anything that will allow me to get at the nose_use_this attribute that I defined in my TestCase-ish class.
EDIT:
I think probably the best way to do this is to get access to the context with a startContext/stopContext method pair, and to set attributes on the instance there:
class MyPlugin(Plugin):
def __init__(self, *args, **kwargs):
super(MyPlugin, self).__init__(self, *args, **kwargs)
self.using_this = None
def startContext(self, context):
if hasattr(context, 'nose_use_this'):
self.using_this = context.nose_use_this
def stopContext(self, context):
self.using_this = None
def startTest(self, test):
if self.using_this is not None:
do_something()
To be really robust you'll probably need to implement a stack of things since "startContext" is called for (at least) modules and classes, but if the only place where the attribute can be set is on the class then I think this simple thing should work. It seems to be working for me. (Nose 1.3.0 and plugin api version 0.10)
original:
Oh, it's the _context() function on the test instance:
ipdb> test._context()
<class 'tests.MyTests'>
And it does indeed have the expected class-level attributes. I'm still open to a better way of doing this, the _ makes me think that nose doesn't want me treating this as part of the API.
I'm writing a class that interfaces to a MoinMoin wiki via xmlrpc (simplified code follows):
class MoinMoin(object):
token = None
def __init__(self, url, username=None, password=None):
self.wiki = xmlrpclib.ServerProxy(url + '/?action=xmlrpc2')
if username and password:
self.token = self.wiki.getAuthToken(username, password)
# some sample methods:
def searchPages(self, regexp):
def getPage(self, page):
def putPage(self, page):
now each of my methods needs to call the relevant xmlrpc method alone if there isn't authentication involved, or to wrap it in a multicall if there's auth. Example:
def getPage(self, page):
if not self.token:
result = self.wiki.getPage(page)
else:
mc = xmlrpclib.MultiCall(self.wiki) # build an XML-RPC multicall
mc.applyAuthToken(self.token) # call 1
mc.getPage(page) # call 2
result = mc()[-1] # run both, keep result of the latter
return result
is there any nicer way to do it other than repeating that stuff for each and every method?
Since I have to call arbitrary methods, wrap them with stuff, then call the identically named method on another class, select relevant results and give them back, I suspect the solution would involve meta-classes or similar esoteric (for me) stuff. I should probably look at xmlrpclib sources and see how it's done, then maybe subclass their MultiCall to add my stuff...
But maybe I'm missing something easier. The best I've come out with is something like:
def _getMultiCall(self):
mc = xmlrpclib.MultiCall(self.wiki)
if self.token:
mc.applyAuthToken(self.token)
return mc
def fooMethod(self, x):
mc = self._getMultiCall()
mc.fooMethod(x)
return mc()[-1]
but it still repeats the same three lines of code for each and every method I need to implement, just changing the called method name. Any better?
Python function are objects so they can be passed quite easily to other function.
def HandleAuthAndReturnResult(self, method, arg):
mc = xmlrpclib.MultiCall(self.wiki)
if self.token:
mc.applyAuthToken(self.token)
method(mc, arg)
return mc()[-1]
def fooMethod(self, x):
HandleAuthAndReturnResult(xmlrpclib.MultiCall.fooMethod, x)
There may be other way but I think it should work. Of course, the arg part needs to be aligned with what is needed for the method but all your methods take one argument.
Edit: I didn't understand that MultiCall was a proxy object. Even if the real method call ultimately is the one in your ServerProxy, you should not pass this method object in case MultiCall ever overrides(define) it. In this case, you could use the getattribute method with the method name you want to call and then call the returned function object. Take care to handle the AttributeError exception.
Methods would now look like:
def HandleAuthAndReturnResult(self, methodName, arg):
mc = xmlrpclib.MultiCall(self.wiki)
if self.token:
mc.applyAuthToken(self.token)
try:
methodToCall = getattr(mc, methodName)
except AttributeError:
return None
methodToCall(arg)
return mc()[-1]
def fooMethod(self, x):
HandleAuthAndReturnResult('fooMethod', x)