The mock method "socket.socket.connect" was called with the specified parameters to approve it while maintaining the functionality
I am using Python 2.7 and mock library to check if a method of an connect
instance of a class object has been called socket.socket
with specific arguments. However, I only want to use the layout as a "marker" and continue to work fine with the function call. In the case below, I want to socket.socket.connect
call the real unplowed function as a "side effect" so the method doesn't break later.
That is, I want the class layout to socket.socket
retain the same functionality and behavior, but with the added ability to record calls.
Here's a (simplified) test. Here's what I think I was wrong:
# test.py
@patch('socket.socket.connect')
@override_settings(SERVER_IP='127.0.0.1')
def test_ip_from_settings(self, connect_mock):
"""
The IP to connect to is taken from the Django settings.
"""
def connect(self, address):
socket.socket.connect(self, address)
connect_mock.side_effect = connect
result = connections.get_result()
connect_mock.assert_called_with(('127.0.0.1', TCP_PORT))
And for your reference, this is the (again simplified) code for connecting and getting results:
# connections.py
from django.conf import settings
def get_result():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((settings.SERVER_IP, TCP_PORT))
query = 'myquery'
s.sendall(query)
result = s.recv(BUFFER_SIZE)
return result
However, it TypeError: connect() takes exactly 2 arguments (1 given)
rises when the test is run . How can I do what I want?
source to share
To mocking unbound method you have to use autospec=True
to save signature ... But unfortunately socket.socket.connect()
can't be mocked because it is C method . But our goal is not to mock it, but simply to fool it with the layout. So the simplest, but not exactly clean trick I have found is to use a new class to falsify the framework
from mock import ANY
class MyS():
def connect(self, address): #The signature
pass
@patch("socket.socket.connect", autospec=MyS.connect, side_effect=socket.socket.connect)
@override_settings(SERVER_IP='127.0.0.1')
def test_ip_from_settings(self, connect_mock):
"""
The IP to connect to is taken from the Django settings.
"""
result = connections.get_result()
connect_mock.assert_called_with(ANY,('127.0.0.1', TCP_PORT))
You have to use ANY
from mock helpers because you don't know what socket object will be passed to your shell.
This trick works for either Python3 or Python2.7, but the behavior is slightly different in Python3 because it is socket.socket.connect()
not a function, but method_descriptor
.
>>> import socket
>>> type(socket.socket.connect)
<class 'method_descriptor'>
Also in this case the usage autospec=True
doesn't work.
The real question is, are you sure you need a real compound to run your tests. Mock goals are disconnecting testing from real resources, registering calls and asserting arguments is a plus, but the first use is to replace a real object with simple, fast computations and can be configured to return what we need to test certain types of behavior.
Perhaps you really need to patch()
socket.socket
and install some return_values
or side_effect
to test your test in the cases that you check.
source to share
An inner function connect
within a method test_ip_from_settings
is not a method, but a function. Therefore, you must remove the first argument self
.
It:
def connect(self, address):
socket.socket.connect(self, address)
Should be:
def connect(address):
socket.socket.connect(address)
This is because of this, when you call s.connect((settings.SERVER_IP, TCP_PORT))
, the tuple (settings.SERVER_IP, TCP_PORT)
is one argument that goes in the argument self
in your case, and then the variable address
still needs to be specified.
In the second correct case, there is no argument self
, so the tuple is bound to the argument address
.
source to share