How to write a unit test with mockito in Python - python

I'm a newbie in Python and I have problem with mockito in Python.
My production code looks like below:
from stompest.config import StompConfig
from stompest.sync import Stomp
class Connector:
def sendMessage(self):
message = {'message'}
dest = '/queue/foo'
def _send(self, message='', dest=''):
config = StompConfig(uri="tcp://localhost:61613")
client = Stomp(config)
client.connect()
client.send(body=message, destination=dest,
headers='')
client.disconnect()
as you see I would like to send a message using Stomp protocol. I my test I would like to test that w when I invoke a send method from Connector class a send method from Stompest library will be only once invoked.
My unit test looks like:
from Connector import Connector
import unittest
from mockito import *
import stompest
from stompest.config import StompConfig
from stompest.sync import Stomp
class test_Connector(unittest.TestCase):
def test_shouldInvokeConnectMethod(self):
stomp_config = StompConfig(uri="tcp://localhost:61613")
mock_stomp = mock(Stomp(stomp_config))
connector = Connector()
connector.sendMessage()
verify(mock_stomp, times=1).connect()
When I run test in debug mode I see that method for instance connect() is invoked and method send as well, but as a result of test I get:
Failure
Traceback (most recent call last):
File "C:\development\systemtest_repo\robot_libraries\test_Connector.py", line 16, in test_shouldInvokeConnectMethod
verify(mock_stomp, times=1).connect()
File "C:\Python27\lib\site-packages\mockito\invocation.py", line 111, in __call__
verification.verify(self, len(matched_invocations))
File "C:\Python27\lib\site-packages\mockito\verification.py", line 63, in verify
raise VerificationError("\nWanted but not invoked: %s" % (invocation))
VerificationError:
Wanted but not invoked: connect()
What did I do wrong?

You don't actually call the connect method on the mock object - you just check that it was called. This is what the error says as well Wanted but not invoked: connect(). Perhaps adding a call to mock_stomp.connect() before the call to verify will fix this:
mock_stomp = mock(Stomp(stomp_config))
# call the connect method first...
mock_stomp.connect()
connector = Connector()
connector.sendMessage()
# ...then check it was called
verify(mock_stomp, times=1).connect()
If you are instead trying to check that the mock is called from Connector, you probably at least need to pass in the mock_stomp object via dependency injection. For example
class Connector:
def __init__(self, stomp):
self.stomp = stomp
def sendMessage(self, msg):
self.stomp.connect()
# etc ...
and in your test
mock_stomp = mock(Stomp(stomp_config))
connector = Connector(mock_stomp)
connector.sendMessage()
verify(mock_stomp, times=1).connect()
Otherwise, I don't see how the connect() method could be invoked on the same instance of mock_stomp that you are basing your assertions on.

Related

Python socketio add namespace after a connection is started

Looking at the python socketio documentation I defined a custom namespace:
import socketio
class MyCustomNamespace(socketio.ClientNamespace):
def on_connect(self):
pass
def on_disconnect(self):
pass
def on_my_event(self, data):
self.emit('my_response', data)
sio = socketio.Client()
sio.register_namespace(MyCustomNamespace('/chat'))
sio.connect("http://127.0.0.1:8888")
sio.emit("get", namespace="/chat")
Now this namespace works as long as I start the connection after registering the namespace. Makes sense.
But is there a way to register namespaces after the connection has started? I get the following error:
File "//src/pipeline/reporting/iam.py", line 30, in request_iam_credentials
self.socket_client.emit(
File "/usr/local/lib/python3.8/dist-packages/socketio/client.py", line 393, in emit
raise exceptions.BadNamespaceError(
socketio.exceptions.BadNamespaceError: /chat is not a connected namespace.
Each namespace is kinda different space you have to connect to. If you don't use the namespace explicitely it's defaulted to '/'. Look:
sio = socketio.Client()
sio.emit("get") # emit before connect
# socketio.exceptions.BadNamespaceError: / is not a connected namespace.
Your situation is the same, but with /chat namespace - you connect to / but try emiting to /chat. You need to connect to /chat namespace by yourself:
sio.connect("http://127.0.0.1:8888", namespaces=["/chat"]) # notice you can connect to more namespaces at once
which is exactly what sio.connect do when you register namespace earlier.

SSHException Error reading SSH protocol banner with ParallelSSHClient

I'm using ParallelSSHClient to connect to multiple servers.
When I'm running the Python function, it is working perfectly.
However, when I'm calling the function from a test case in Robot Framework, I'm getting the following error.
SSHException: Error reading SSH protocol banner('This operation would
block forever', )
The Python function I have used is:
from pssh.pssh_client import ParallelSSHClient
from pssh.utils import load_private_key
from robot.libraries.BuiltIn import BuiltIn
def check101():
pkey = load_private_key('/root/test.pem')
hosts = ['2.2.2.2', '1.1.1.1']
client = ParallelSSHClient(hosts, pkey=pkey)
try:
output = client.run_command("<command>")
except (AuthenticationException):
print 'Error'
node=0
for host in output:
for line in output[host].stdout:
node=node+1
if (int(line)>0):
return node
break
return -1
Add the following at the start-
from gevent import monkey
monkey.patch_all()

Python: block network connections for testing purposes?

I'm trying to test a package that provides interfaces to a few web services. It has a test suite that is supposed to test most functions without connecting to the internet. However, there are some lingering tests that may attempt to connect to the internet / download data, and I'd like to prevent them from doing so for two reasons: first, to make sure my test suite works if no network connection is available; second, so that I'm not spamming the web services with excess queries.
An obvious solution is to unplug my machine / turn off wireless, but when I'm running tests on a remote machine that obviously doesn't work.
So, my question: Can I block network / port access for a single python process? ("sandbox" it, but just blocking network connections)
(afaict, pysandbox doesn't do this)
EDIT: I'm using py.test so I need a solution that will work with py.test, in case that affects any proposed answers.
Monkey patching socket ought to do it:
import socket
def guard(*args, **kwargs):
raise Exception("I told you not to use the Internet!")
socket.socket = guard
Make sure this runs before any other import.
Update: There is now a pytest plugin that does the same thing as this answer! You can read the answer just to see how things work, but I strongly recommend using the plugin instead of copying-pasting my answer :-) See here: https://github.com/miketheman/pytest-socket
I found Thomas Orozco's answer to be very helpful. Following on keflavich, this is how I integrated into my unit test suite. This works for me with thousands of very different unit test-cases (<100 that need socket though) ... and in and out of doctests.
I posted it here. Including below for convenience. Tested with Python 2.7.5, pytest==2.7.0. (To test for yourself, run py.test --doctest-modules in directory with all 3 files cloned.)
_socket_toggle.py
from __future__ import print_function
import socket
import sys
_module = sys.modules[__name__]
def disable_socket():
""" disable socket.socket to disable the Internet. useful in testing.
.. doctest::
>>> enable_socket()
[!] socket.socket is enabled.
>>> disable_socket()
[!] socket.socket is disabled. Welcome to the desert of the real.
>>> socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Traceback (most recent call last):
...
RuntimeError: I told you not to use the Internet!
>>> enable_socket()
[!] socket.socket is enabled.
>>> enable_socket()
[!] socket.socket is enabled.
>>> disable_socket()
[!] socket.socket is disabled. Welcome to the desert of the real.
>>> socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Traceback (most recent call last):
...
RuntimeError: I told you not to use the Internet!
>>> enable_socket()
[!] socket.socket is enabled.
"""
setattr(_module, '_socket_disabled', True)
def guarded(*args, **kwargs):
if getattr(_module, '_socket_disabled', False):
raise RuntimeError("I told you not to use the Internet!")
else:
# SocketType is a valid public alias of socket.socket,
# we use it here to avoid namespace collisions
return socket.SocketType(*args, **kwargs)
socket.socket = guarded
print(u'[!] socket.socket is disabled. Welcome to the desert of the real.')
def enable_socket():
""" re-enable socket.socket to enable the Internet. useful in testing.
"""
setattr(_module, '_socket_disabled', False)
print(u'[!] socket.socket is enabled.')
conftest.py
# Put this in the conftest.py at the top of your unit tests folder,
# so it's available to all unit tests
import pytest
import _socket_toggle
def pytest_runtest_setup():
""" disable the interet. test-cases can explicitly re-enable """
_socket_toggle.disable_socket()
#pytest.fixture(scope='function')
def enable_socket(request):
""" re-enable socket.socket for duration of this test function """
_socket_toggle.enable_socket()
request.addfinalizer(_socket_toggle.disable_socket)
test_example.py
# Example usage of the py.test fixture in tests
import socket
import pytest
try:
from urllib2 import urlopen
except ImportError:
import urllib3
urlopen = urllib.request.urlopen
def test_socket_disabled_by_default():
# default behavior: socket.socket is unusable
with pytest.raises(RuntimeError):
urlopen(u'https://www.python.org/')
def test_explicitly_enable_socket(enable_socket):
# socket is enabled by pytest fixture from conftest. disabled in finalizer
assert socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Building on the very helpful answers from Thomas Orozco and driftcatcher here is a variant that works with Python's unittest and (after a small change) Django.
All you need to do is inherit your test case class from the enhanced NoSocketTestCase class and any access to the network will be detected and raises the SocketAccessError exception.
And this approach also works with Django. You only need to change the NoSocketTestCase class to inherit from django.test.TestCase instead of unittest.TestCase.
While not strictly answering OP's question I think this might be helpful for anyone who wants to block network access in unit tests.
no_sockets.py
import socket
from unittest import TestCase
class SocketAccessError(Exception):
pass
class NoSocketsTestCase(TestCase):
"""Enhancement of TestCase class that prevents any use of sockets
Will throw the exception SocketAccessError when any code tries to
access network sockets
"""
#classmethod
def setUpClass(cls):
cls.socket_original = socket.socket
socket.socket = cls.guard
return super().setUpClass()
#classmethod
def tearDownClass(cls):
socket.socket = cls.socket_original
return super().tearDownClass()
#staticmethod
def guard(*args, **kwargs):
raise SocketAccessError('Attempted to access network')
test_no_sockets.py
import urllib.request
from .no_sockets import NoSocketsTestCase, SocketAccessError
class TestNoSocketsTestCase(NoSocketsTestCase):
def test_raises_exception_on_attempted_network_access(self):
with self.assertRaises(SocketAccessError):
urllib.request.urlopen('https://www.google.com')
A simple way to put a gag on the requests library:
from unittest import mock
requests_gag = mock.patch(
'requests.Session.request',
mock.Mock(side_effect=RuntimeError(
'Please use the `responses` library to mock HTTP in your tests.'
))
)
with requests_gag:
... # no Internet here
httpretty is a small library that solves this problem.
If you are using Django test runner, write a custom test runner where you disable all 3rd party API calls.
# common/test_runner.py
import httpretty
from django.test.runner import DiscoverRunner
class CustomTestRunner(DiscoverRunner):
def run_tests(self, *args, **kwargs):
with httpretty.enabled(allow_net_connect=False):
return super().run_tests(*args, **kwargs)
add this new test runner to your settings
TEST_RUNNER = "common.test_runner.CustomTestRunner"
And from now on all external API calls have to be mocked or httpretty.errors.UnmockedError will be raised.
If you are using pytest, this fixture should work.
#pytest.fixture
def disable_external_api_calls():
httpretty.enable()
yield
httpretty.disable()
I have a pytest solution. pytest-network lybrary help me on this.
# conftest.py
import pytest
import socket
_original_connect = socket.socket.connect
def patched_connect(*args, **kwargs):
...
# It depends on your testing purpose
# You may want a exception, add here
# If you test unconnectable situations
# it can stay like this
#pytest.fixture
def enable_network():
socket.socket.connect = _original_connect
yield
socket.socket.connect = patched_connect
#pytest.fixture
def disable_network():
socket.socket.connect = patched_connect
yield
socket.socket.connect = _original_connect
# test_internet.py
def test_your_unconnectable_situation(disable_network):
response = request.get('http://stackoverflow.com/')
response.status_code == 400

django unittest without database connection

I'm trying to write a unittest that will check if the correct error message is returned in case the database connection hits exception. I've tried to use connection.creation.destroy_test_db(':memory:') but it didn't work as I expected. I suppose I should either remove the tables or somehow cut the db connection. Is any of those possible?
I found my answer in the presentation Testing and Django by Carl Meyer. Here is how I did it:
from django.db import DatabaseError
from django.test import TestCase
from django.test.client import Client
import mock
class NoDBTest(TestCase):
cursor_wrapper = mock.Mock()
cursor_wrapper.side_effect = DatabaseError
#mock.patch("django.db.backends.util.CursorWrapper", cursor_wrapper)
def test_no_database_connection(self):
response = self.client.post('/signup/', form_data)
self.assertEqual(message, 'An error occured with the DB')
Sounds like this is a job for mocking. For example, if you are using MySQL, you can put a side_effect on connect method, like this:
from django.test import TestCase
from mock import patch
import MySQLdb
class DBTestCase(TestCase):
def test_connection_error(self):
with patch.object(MySQLdb, 'connect') as connect_method:
connect_method.side_effect = Exception("Database Connection Error")
# your assertions here
Hope that helps.
Since dec, 2021 there is the library Django Mockingbird.
With this you can mock the object that would be retrieved from db.
from djangomockingbird import mock_model
#mock_model('myapp.myfile.MyModel')
def test_my_test():
some_test_query = MyModel.objects.filter(bar='bar').filter.(foo='foo').first()
#some more code
#assertions here
I was looking for django's actual http response code in case of a database connection timeout when using pymysql. The following test confirmed it's a 401 Unauthorized when pymysql raises an OperationalError.
from unittest.mock import patch
import pymysql
from django.test import TestCase, Client
class TestDatabaseOutage(TestCase):
client = None
def setUp(self):
self.client = Client()
def test_database_connection_timeout_returns_401(self):
with patch.object(pymysql, 'connect') as connect_method:
message = "Can't connect to MySQL server on 'some_database.example.com' ([Errno 110] Connection timed out)"
connect_method.side_effect = pymysql.OperationalError(2003, message)
response = self.client.get('/')
self.assertEqual(response.status_code, 401)

Safe way to connect to RPC server

This question is related to How do we handle Python xmlrpclib Connection Refused?
When I try to use the following code, with my RPC server down, _get_rpc() returns False and I'm good to go. However if the server is running, it fails with unknown method. Is it trying to execute .connect() on the remote server? How can I get around this, when I needed to use .connect() to detect if the returned proxy worked (see related question)?
import xmlrpclib
import socket
def _get_rpc():
try:
a = xmlrpclib.ServerProxy('http://dd:LNXFhcZnYshy5mKyOFfy#127.0.0.1:9001')
a.connect() # Try to connect to the server
return a.supervisor
except socket.error:
return False
if not _get_rpc():
print "Failed to connect"
Here is the issue:
ahiscox#lenovo:~/code/dd$ python xmlrpctest2.py
Failed to connect
ahiscox#lenovo:~/code/dd$ supervisord -c ~/.supervisor # start up RPC server
ahiscox#lenovo:~/code/dd$ python xmlrpctest2.py
Traceback (most recent call last):
File "xmlrpctest2.py", line 13, in <module>
if not _get_rpc():
File "xmlrpctest2.py", line 7, in _get_rpc
a.connect() # Try to connect to the server
File "/usr/lib/python2.6/xmlrpclib.py", line 1199, in __call__
return self.__send(self.__name, args)
File "/usr/lib/python2.6/xmlrpclib.py", line 1489, in __request
verbose=self.__verbose
File "/usr/lib/python2.6/xmlrpclib.py", line 1253, in request
return self._parse_response(h.getfile(), sock)
File "/usr/lib/python2.6/xmlrpclib.py", line 1392, in _parse_response
return u.close()
File "/usr/lib/python2.6/xmlrpclib.py", line 838, in close
raise Fault(**self._stack[0])
xmlrpclib.Fault: <Fault 1: 'UNKNOWN_METHOD'>
Well i was just looking in to it ; my old method suck because xmlrpclib.ServerProxy try to connect to the XmlRPC server when you call a method, not before !!!
Try this instead :
import xmlrpclib
import socket
def _get_rpc():
a = xmlrpclib.ServerProxy('http://dd:LNXFhcZnYshy5mKyOFfy#127.0.0.1:9001')
try:
a._() # Call a fictive method.
except xmlrpclib.Fault:
# connected to the server and the method doesn't exist which is expected.
pass
except socket.error:
# Not connected ; socket error mean that the service is unreachable.
return False, None
# Just in case the method is registered in the XmlRPC server
return True, a
connected, server_proxy = _get_rpc():
if not connected
print "Failed to connect"
import sys
sys.exit(1)
To summarize this we have 3 cases :
XmlRPC server is up and in it we defined a method called _():
(EDIT : i did choose the name _ because it unlikely to have a method with this name, but this case still can happen)
In this case no exception will be catch and the code will execute
the return True
XmlRPC server is up and in it we don't have any method methoded call
_():
This time xmlrpclib.Fault will be raised and we will also pass to the return True
XmlRPC server is down:
Now the socket.error exception will be raised and that
when we call a._() so we should return False
I don't know if there is an easy way to do this and i will love to see it until then , hope this can fix thing this time :)
N.B: when you do if a: python will again search for a method __nonzero__() to test the boolean value of a and this will fail to.
N.B 2: Some xmlrpc service offer a rpc path specialized to do an authentication , in this path the service offer methods like login() ... , this kinds of method can replace the _() method in our case, so just calling login(), will be enough to know if the service is up or down (socket.error), and in the same time this login() method authenticate the user if the service is up .

Categories

Resources