简体   繁体   中英

Mocking Communications

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM