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:
Make use of dependency injection by changing the __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.
Make use of a extremely powerful mocking/patching tool called MonkeyPatch which actually can patch/mock instance variables of classes. This approach would have been like trying to kill a fly with a rocket launcher.
You're on the right track, but there are a couple things that need to change for this test to work.
Part of your problem right off the bat is that the mock that patch passes into your test method is called mockSocket, but your test code is referring to something called mock_socket.
Also, patch's first argument, the thing you want to patch, should be a string representation of the path to the module where you want to patch something. If your file structure looks like this:
|-- root_directory
| |
| |-- app_directory
| | |-- socketHandler.py
| | `-- somethingElse.py
| |
| `-- test_directory
| |-- testSocketHandler.py
| `-- testSomethingElse.py
and you run your tests from the root directory, you'd want to call patch like this: #mock.patch("app_directory.socketHandler.socket")
Constructor is called - The most important thing to realize is that mockSocket is a Mock object representing the socket class. So to test that the constructor was called, you need to check mockSocket.assert_called_with(...). That will pass if your production calls socket(...).
You may also want to assert that mySocketHandler.socket is the same object as mockSocket.return_value, to test that mySocketHandler not only calls the constructor, but assigns it to the right attribute.
and 3. connect and sendall are called properly - You should never call your mock in a test, because it can lead to falsely-passing assertions. In other words, you want your production code to be the only thing calling mocks. This means you shouldn't use the line new_sock = mock_socket(), because then your previous assertion about the constructor will pass no matter what your production code does, and I think it's causing your other assertions to fail.
mockSocketis already an instance of Mock, so it's return value will automatically be another, different Mock instance. Therefore, you don't need the first 2 lines of your test code above, and you only need one of the assertions on connect. The same ideas apply to sendall.
That's a lot to take in, here's what your test would look like if I wrote it:
from unittest import mock, TestCase
import pytest
import socketHandler
class TestSocketHandler(TestCase):
#mock.patch("app_directory.socketHandler.socket")
def test_socket_handler(mockSocketClass): # renamed this variable to clarify that it's a mock of a class.
# mockSocketClass is already a mock, so we can call production right away.
mySocketHandler = SocketHandler(...)
# Constructor of mockSocketClass was called
mockSocketClass.assert_called_with(...)
# Instance of mockSocketClass was assigned to correct attribute on SocketHandler
self.assertIs(mockSocketClass.return_value, mySocketHandler.socket)
# Production called connect on the return_value of the mock module, i.e. the instance of socket.
mockSocketClass.return_value.connect.assert_called_with(...)
# If SocketHandler's constructor calls sendall:
mockSocketClass.return_value.sendall.assert_called_with(expectedMessage)
Bonus Round! MagicMocks behave like Mocks, except that they implement some default values for some magic methods. I don't use them unless I absolutely need them. Here's an example:
from mock import Mock, MagicMock
mock = Mock()
magic_mock = MagicMock()
int(mock)
>>>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string or a number, not 'Mock'
len(mock)
>>>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'Mock' has no len()
int(magic_mock)
>>> 1
len(magic_mock)
>>> 0
Related
I am using Python's mock library along with unittest. I am writing unit tests for a class that uses a function of an external library in one of its methods. Depending on the case, this function returns different values.
So let's say I wanna test class A:
from external_library import function_foo
class A(object):
...
In my test class, in order to use the values returned by the function from the external library, I create a patch, and only import class A after defining the patch. However, I need to use this function in all my test methods, and in each method it returns different values.
My test class is as follows:
class TestA(TestCase):
#patch('external_library.function_foo', side_effect=[1, 2, 3])
def test_1(self, *patches):
from module import class A
obj = A()
...
#patch('external_library.function_foo', side_effect=[1, 1, 2, 2, 3, 3])
def test_2(self, *patches):
from module import class A
obj = A()
...
...
I have 10 tests and only 1 (the first one) passes when I run all of them together, for the rest, I get StopIteration error. However, if I run each one of them individually, they all pass.
I have tried using with patch('external_library.function_foo', side_effect=[...]) in each method, but the outcome was the same. I also tried creating only once the patch in the setUp method, starting it, reassigning the side_effect within each method, and stopping in tearDown, but it didn't work.
Any ideas on what might work in this case?
Thanks!
The caveat is, the second time you import a module, it would not be loaded again, you get the same module object as the first time you imported.
When you first run "test_1", external_library.function_foo replaced by a Mock object, let's name it mock_a. Then your "module" get imported for the first time, python will load it, means, execute code inside "module", which will bind name "function_foo" to object "mock_a" in the namespace of "module", save "module" object to sys.modules. This time your test will pass, and side_effect of mock_a get consumed.
Next is "test_2", external_library.function_foo replaced by a Mock object, name it to mock_b. Then import "module", this time it would not be loaded again, but populate from sys.modules, you get the same module object as in "test_1". In the namespace of this module object, name "function_foo" is still bound to object mock_a, not the newly created mock_b. Because side_effect of mock_a has already consumed, StopIteration error raised.
You should apply patch to where a name is looked up, but not where it is defined:
#patch('module.function_foo', side_effect=[1, 2, 3])
def test_1(self, patch):
...
Read more detail on the "Where to patch" section of the manual of patch.
I must be tired, because surely there is an easy way to do this.
But I've read over the pytest docs and can't figure out this simple use case.
I have a little package I want to test:
class MyClass:
def __init__(self):
pass
def my_method(self, arg):
pass
def the_main_method():
m = MyClass()
m.my_method(123)
I would like to ensure that (1) an instance of MyClass is created, and that (2) my_method is called, with the proper arguments.
So here's my test:
from unittest.mock import patch
#patch('mypkg.MyClass', autospec=True)
def test_all(mocked_class):
# Call the real production code, with the class mocked.
import mypkg
mypkg.the_main_method()
# Ensure an instance of MyClass was created.
mocked_class.assert_called_once_with()
# But how do I ensure that "my_method" was called?
# I want something like mocked_class.get_returned_values() ...
I understand that each time the production code calls MyClass() the unittest framework whips up a new mocked instance.
But how do I get my hands on those instances?
I want to write something like:
the_instance.assert_called_once_with(123)
But where do I get the_instance from?
Well, to my surprise, there is only one mock instance created, no matter how many times you call the constructor (:
What I can write is:
mocked_class.return_value.my_method.assert_called_once_with(123)
The return_value does not represent one return value, though — it accumulates information for all created instances.
It's a rather abstruse approach, in my mind. I assume it was copied from some crazy Java mocking library (:
If you want to capture individual returned objects, you can use .side_effect to return whatever you want, and record it in your own list, etc.
So I have a python 2.7 project with three modules. Two contain classes and one is a script. An example files structure is shown below
project/
__main__.py
__init__.py
- serial_connect/
ser_conn.py
__init__.py
- ui/
parse_file.py
__init__.py
ser_conn.py contains a class which handles all interaction with some RS-232 hardware device. It contains methods such as open(), close(), send_go() just basically everything required for this connection.
parse_file.py contains a class which has methods relating to parsing a file and getting text commands which are associated with serial commands.
e.g. if the text file contains the command "send_go" parse_file.py will parse this command and call Ser_Conn.send_go()
__main.py__ contains the main() function. in main() an instance of the Ser_Conn class is created and then passed to the Parse_File class as there exists only one instance of a serial connection in the program and it is required by both main() and the Parse_File class.
My question is as follows
In methods of Parse_File I call methods of the Ser_Conn instance, such as Ser_Conn.send_go() but parse_file.py does not complain about there being no
from serial_connect.ser_conn import Ser_Conn
There only exists a ser_conn import in __main__.py. why does this work?
In python, an instance carries all the "stuff" that it needs to do it's work along with it. At a high level, (and somewhat simplified) when you write:
qux.whatever
python looks at the object qux and then finds its whatever attribute (if it has one). Note that python doesn't care what type qux is, only that it has a whatever attribute. I could create multiple objects that satisfy this interface:
class Foo(object):
whatever = 'Hey Dawg!'
class Bar(object):
whatever = 'I satisfy the interface too!'
Also note that these objects could be defined anywhere. As long as you manage to get a reference to the object (e.g. it was passed into a function) you can use the object with all of it's attributes and methods.
maybe a more concrete example would help -- Say you have 4 modules, foo, bar, call_method and driver. foo, bar and call_method don't know anything about each other, but driver imports the other 3. Then you can do something like this:
# foo.py
class Foo(object):
def method(self):
return 'foo!'
# bar.py
class Bar(object):
def method(self):
return 'bar!'
# call_method.py
def go(object_with_method):
print(object_with_method.method())
# driver.py
import call_method
import foo
import bar
call_method.go(Foo()) # call the method on a foo instance
call_method.go(Bar()) # call the method on a bar instance
You're passing the connection instance to parse_file which means Python already knows the class and other details of that object. So that's why you don't need to import the class again in the parse_file code.
You only need to import something if you wish to use that something in that file.
When you created the object Python used the class to construct the object and that's sufficient. You can of course add that import line in parse_file but only if you actually need to use the class in that file (otherwise the import line it's very useful).
I've read all I can find with regards to Python 3 and mocking. Unfortunately, I still can't figure out why I need to verify assertions on mock() instead of mock when all documentation I've read use the latter.
Here is what I test looks like -
from unittest.mock import Mock, patch
from unittest import TestCase, skip
from suds.client import Client, ServiceDefinition
from run import WebService
import unittest
#patch('run.Client')
def test(self, mock):
service = WebService()
weather_id = 1234
weather = service.get_weather(weather_id)
mock().service.GetWeather.assert_called_once_with(weather_id)
run in this case is where WebService resides and Client is the suds client.
Printing mock.mock_calls I see -
[
call('SERVICE_WSDL', proxy={'https': 'PROXY', 'http': 'PROXY'}),
call().factory.create('AuthHeader'),
call().set_options(soapheaders=<MagicMock name='Client().factory.create()' id='37553264'>),
call().service.GetWeather(1234, '', '')
]
Mind you, my test passes. I'm simply wondering what I'm missing so I can better understand mocking in Python.
First of all, let's rename the variable because it's actually a mocked instance of run.Client:
#patch('run.Client')
def test(self, mock_client):
# ...
mock_client().service.GetWeather.assert_called_once_with(weather_id)
You're creating an instance of Client within run and you're using that in your code. But you're not actually mocking that instance in this test, you're mocking the class (run.Client is being patched).
So we have a mocked class and we're calling that class to create an instance. That instance is what your code actually uses. This means what you really want is access to the return value of the constructor of the class:
mock_client.return_value.service.GetWeather.assert_called_once_with(weather_id)
This is the same as your code except without calling mock_client(). Your current test code does something similar: call the mocked class, see what instance you get back and perform assertions on that. Yet the mock library provides the return_value attribute to do that already.
First time using patch. I've tried to patch one of my classes for testing. Without the patch attempting to run gets past the test function definition, but with the patch the test function definition apparently requires another parameter and I get a
TypeError: testAddChannelWithNamePutsChannel() takes exactly 1 argument (2 given)
Error. The test code is the following:
import unittest
import mock
from notification.models import Channel, addChannelWithName, deleteChannelWithName
class TestChannel(unittest.TestCase):
#mock.patch('notification.models.Channel')
def testAddChannelWithNamePutsChannel(self):
addChannelWithName('channel1')
Channel.put.assert_called_with()
Why does it require an extra parameter with the patch and what should this parameter be? Thank you much!
Patch passes in an instance of the patched object to your test method (or to every test method if you are patching at the class level). This is handy because it lets you set return values and side effects, or check the calls made
from unittest.mock import patch
#patch('some_module.sys.stdout')
def test_something_with_a_patch(self, mock_sys_stdout):
mock_sys_stdout.return_value = 'My return value from stdout'
my_function_under_test()
self.assertTrue(mock_sys_stdout.called)
self.assertEqual(output, mock_sys_stdout.return_value)
If you just want to literally patch something out to ignore it then you can call patch with the following invocation
from unittest.mock import patch, Mock
#patch('some_module.sys.stdout', Mock())
def test_something_with_a_patch(self):
That replaces sys.stdout in the some_module with a Mock object and does not pass it to the method.
patch passes the patched object to the test function. Its documented here:
patch as function decorator, creating the mock for you and passing it
into the decorated function:
>>>
>>> #patch('__main__.SomeClass')
... def function(normal_argument, mock_class):
... print(mock_class is SomeClass)
...
>>> function(None)
True