Mocking Communications
I am creating an application in python that uses a wrapper in a library that does hardware communication
I would like to create some test units, and I am fairly new to unit tests, so I would like to poke fun at the posts, but I really donβt know how.
quick example:
this is the application code using comm lib
def changeValue(id, val):
current_value = comm.getval(id)
if (current_value != val):
comm.send(id, val)
I want to test this without doing any communication i.e. replacing comm.getval's return with some mock value and sending comm.send to the generated comm class.
Can anyone tell me this?
The point is that comm is an object inside a class
let's say the class is like this:
class myClass:
comm = Comm()
....
def __init__():
comm = comm.start()
def changeValue(id, val):
....
....
source to share
You can use the mock
framework for this kind of job. First of all, you are using
comm = Comm()
in MyClass
, which means that you have something like from comm_module import Comm
in a module MyClass
. In these cases, you need to correct the link Comm
in the module MyClass
to make the patch active.
So an example of how you can test your code without any connection could be:
@patch("my_class.Comm", autospec=True)
def test_base(self, mock_comm_factory):
mock_comm = mock_comm_factory.return_value
MyClass()
mock_comm.start.assert_called_with()
@patch("my_class.Comm", autospec=True)
def test_changeValue(self, mock_comm_factory):
mock_comm = mock_comm_factory.return_value
mock_comm.getval.return_value = 13
MyClass().changeValue(33, 23)
mock_comm.getval.assert_called_with(33)
mock_comm.send.assert_called_with(33, 23)
mock_comm.reset_mock()
mock_comm.getval.return_value = 23
MyClass().changeValue(33, 23)
mock_comm.getval.assert_called_with(33)
self.assertFalse(mock_comm.send.called)
Now I can start to explain all the details of my answer like why to use autospec=True
or how to apply the patch for all methods , but that means rewriting a lot of mock
SO docs and answers. So I hope this is enough as a starting point.
source to share
The trick is not to use global objects like comm
. If possible, make it comm
injected by your class or method by the caller. Then what you do is convey mocking comm
when testing, and then real when building.
Thus, either you create a link comm
in your class field (and inject it using a constructor or setter method) like so:
class myClass:
....
def __init__(myComm):
comm = myComm;
comm = comm.start()
def changeValue(id, val):
current_value = comm.getval(id)
if (current_value != val):
comm.send(id, val)
....
or you will make it a parameter in the method in which it is used, like
def changeValue(id, val, myComm):
current_value = myComm.getval(id)
if (current_value != val):
myComm.send(id, val)
Using globally nothing mocks is a huge pain, try to use Dependency Injection when you need to mock something.
This is another good article on DI. It's in java, but it should be the same in python http://googletesting.blogspot.ca/2008/07/how-to-think-about-new-operator-with.html
source to share