I am building an application in python that uses a wrap to a library that performs hardware communication
I would like to create some test units and I am pretty new to unit tests, so I would like to mock the communications but I really don't know how to do it
quick example:
this is the application code using the 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 performing communications, ie replacing the comm.getval return by some mocked value, and sending comm.send to a mocked comm class.
Can anyone give a hint on that?
The thing is that comm is a 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):
....
....
You can use mock
framework to this kind of jobs. First of all you use comm = Comm()
in MyClass
and that means you have something like from comm_module import Comm
in MyClass
's module. In these cases you need to patch Comm
reference in MyClass
's module to make your patch active.
So an example of how you can test your code without do 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 details of my answer like why use autospec=True
or how to apply patch to all methods but that means to rewrite a lot of mock
documentations and a SO answers. So I hope that is enough as starting point.
The trick is not to use global objects like comm
. If you can, make it so that comm
gets injected to your class or method by the caller. Then what you do is pass a mocked comm
when testing and then real one when in production.
So either you make a comm
reference a field in your class (and inject it via 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 make it a parameter in the method where it is used, like so
def changeValue(id, val, myComm):
current_value = myComm.getval(id)
if (current_value != val):
myComm.send(id, val)
Using global anything makes mocking a huge pain, try to use Dependency Injection whenever you need to mock something.
This is another good post about DI. It is in java, but it should be the same in python http://googletesting.blogspot.ca/2008/07/how-to-think-about-new-operator-with.html
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.