How to test that a function is called within a fun

2019-04-08 13:50发布

问题:

I'm trying to set up some automatic unit testing for a project. I have some functions which, as a side effect occasionally call another function. I want to write a unit test which tests that the second function gets called but I'm stumped. Below is pseudocode example:

def a(self):
    data = self.get()
    if len(data) > 3500:
        self.b()

    # Bunch of other magic, which is easy to test.

def b(self):
    serial.write("\x00\x01\x02")

How do I test that b()-gets called?

回答1:

You can mock the function b using mock module and check if it was called. Here's an example:

import unittest
from mock import patch


def a(n):
    if n > 10:
        b()

def b():
    print "test"


class MyTestCase(unittest.TestCase):
    @patch('__main__.b')
    def test_b_called(self, mock):
        a(11)
        self.assertTrue(mock.called)

    @patch('__main__.b')
    def test_b_not_called(self, mock):
        a(10)
        self.assertFalse(mock.called)

if __name__ == "__main__":
    unittest.main()

Also see:

  • Assert that a method was called in a Python unit test
  • Mocking a class: Mock() or patch()?

Hope that helps.



回答2:

Various solutions are possible for this thing. But first, I have to point out that unit testing is meant for testing units; what a unit is can be a matter of viewing things. In your case I'd say a unit is the function a() because you would like to test that your unit behaves correctly, meaning it calls function b() at the right spot. A different view would be to say that functions a() and b() are a unit. Then you do not like to check whether function b() gets called, you just want to test the result of the call of function a(). So make up your mind which suits your case best.

In case you really want to test function a() as a unit, you should prepare your unit to being tested. In your case that could be done by adding to it an additional argument (which defaults to function b) and which will be used instead of the hard-coded function b() call in a():

def a(self, subfunction=None):
    is subfunction is None:  # for testing
        subfunction = self.b
    data = self.get()
    if len(data) > 3500:
        subfunction()

Now you can inject (during testing) a helper function which just informs the test that it got called:

store = None
def injected(*args, **kwargs):
    global store
    store = args, kwargs

obj.a(subfunction=injected)
# now check if global store is set properly