In the constructor of my class under test a socket object is instantiated and assigned to a class member. I mocked the socket class and set a mocked socket object as return value to the socket constructor call. I then want to assert that connect() and sendall() is called on that object. I always get the assert error that the functions are not called when I assert on the original mocked class object or the one that I set to return on constructor call.
I know I can’t mock the class that is under test (and its members) because that would defeat the purpose here.
Pseudo code:
import socket
Class socketHandler():
def __init__(...):
self.mySocket = socket(...)
...
self.mySocket.connect(...)
def write(message):
self.mySocket.sendall(message)
Test:
from unittest import mock
from unittest.mock import MagicMock #not sure if i need this
import pytest
import socketHandler
@mock.patch(socketHandler.socket)
def test_socket_handler(mockSocket):
...
new_sock = mock_socket()
mock_socket.return_value = new_sock
mySocketHandler = SocketHandler(...)
mock_socket.socket.assert_called_with(...)
new_sock.connect.assert_called_with(...) #fails (never called)
mock_socket.connect.assert_called_with(...) #fails (never called)
#likewise for the sendall() method call when mysocketHandler.write(..)
#is called
The purpose of this test is:
ensure the constructor of socket library is called with the right arguments.
ensure that connect() is called with right arguments.
ensure that sendall() is called with exactly what I want it to be called, when I pass message into mySocketHandler.write() method.
The complete answer derived from hints given by @ryanh119 and this post link
I will fix the example given above by ryanh119 and refrain from editing original question which i messed up, so for completeness:
from unittest import mock
import pytest
import socketHandler
@mock.patch("app_directory.socketHandler.socket")
def test_socket_handler(mockSocketClass):
# mockSocketClass is already a mock, so we can call production right away.
mySocketHandler = SocketHandler(...)
# Constructor of mockSocketClass was called, since the class was imported
#like: import socket we need to:
mockSocketClass.socket.assert_called_with(...)
# Production called connect on the class instance variable
# which is a mock so we can check it directly.
# so lets just access the instance variable sock
mySocketHandler.mySocket.connect.assert_called_with(...)
# The same goes for the sendall call:
mySocketHandler.mySocket.sendall.assert_called_with(expectedMessage)
I also did some research and there would have been two more solutions that I want to mention. They are not as pythonicaly correct like the above ones but here it is:
__init__
of socketHandler to take in a socket object and only instantiate it if not supplied in the args. That way i could have passed in a mock or MagicMock object and used that to do the asserts on.