Home > database >  Can you easily test whether a method definition has changed, but a call to the method has not been u
Can you easily test whether a method definition has changed, but a call to the method has not been u

Time:12-01

I have the following two methods (simplified):

def func_a(u, v, w, x, y, z):
    ##Do some irrelevant stuff
    newVariable = func_b(u, v, w, y)
    ##Do some more irrelevant stuff

def func_b(u, v, w, y):
    ##Do some manipulation of the variables
    ##Call some more methods, like func_c, func_d, func_e
    newVariable = 1 #In reality; some value that is calculated
    return newVariable 

func_a is fairly simple, but func_b calls a few different methods according to some criteria, which are not relevant here. This function has a high probability of changing in the future, since it features calculations that might be changed at a later stage of the project. My problem is the following:

With Pytest and Mock, we can easily test the functionality of func_a; you can mock func_b and set the return_value.

It is also easy to check whether func_b is called correctly from func_a; with assert_called_with we can make sure the expected values are used when calling the function like so:

@patch('func_b') 
def test_func_a_calls_func_b_correctly(b_mock):
    func_a(1,2,3,4,5,6)
    b_mock.assert_called_with(1, 2, 3, 5)

However, if we change the definition of func_b (for example to: func_b(u, v, w, y, q), this test will still pass, since the call in func_a is still "as expected". This is of course not what we want, since the test will pass, but the program will crash when running it. Is there a way to test for a change of a method definition in this way?

If there is not, is the best possible test this NOT mocking func_b, but mocking func_c, func_d, and func_e that are called in func_b? This will of course work fine, but it does not seem very clean for a unit test.

CodePudding user response:

This is a philosophical question as much as anything - mock isn't designed to emulate the function it is replacing, just provide a fake input and output to see how it is called.

If you do need to ensure that the calls your code makes to a function still 'match' the behaviour of the function, then you have to compare the original function to the mock (or the call) at the time of testing.

For example, in your current example, you could do:

from inspect import signature

@patch('func_b') 
def test_func_a_calls_func_b_correctly(b_mock):
    func_a(1,2,3,4,5,6)

    # Check that the number of params passed when calling func_b
    # is the same as the number of parameters that the 'real' func_b expects
    assert len(mock.call_args.args) == len(signature(func_b).parameters)

However, that doesn't protect against the order or 'purpose' of parameters changing, which would require further inspection of func_b.

  • Related