Home > other >  mock on function with optional arguments
mock on function with optional arguments

Time:12-15

I want to write a test for "A" function which calls another "B" function in it. B function looks like this:

def Bfuncion(self, city: Optional[str], name: Optional[str]):

in A function I use B function two times, first time I use it in this way:

cities = self.Bfunction(name=None, city="ny", page=page)

and the second time in this way:

cities = self.Bfunction(name="Tom", city=None, page=page)

Now I want to write a test of A function, I would try this if there was only one use of B function:

mocker.patch(
        "Path.To.My.Function",
        return_value=[
            {"someReturnValue"},
            {"someReturnValue"},
            {"someReturnValue"},
        ],

How do I write call mocker(), as I use B function with different arguments in each call.

CodePudding user response:

Use the side_effect keyword argument, not the return_value keyword argument.

>>> from unittest.mock import Mock
>>> m = Mock(return_value=[1,2,3])
>>> m()
[1, 2, 3]
>>> m = Mock(side_effect=[1,2,3])
>>> m()
1
>>> m()
2
>>> m()
3

(patch, of course, simply passes keyword arguments it does not itself recognize to Mock to configure the object that is used by the patched name.)

This requires you to know ahead of time the order of calls that will be made. If you need more flexibility, patch the name with a custom function that behaves the way you want, instead of a Mock object.

def my_func(self, name, city, page):
    ...
mocker.patch(
    "Path.To.My.Function",
    new=my_func
)

CodePudding user response:

I try to suggest you a solution for your test; I don't know if it is suite for your need because I have tried to guess some details that you have omitted in your question.

I suppose that your production code is contained in a file called functions.py and the content of functions.py is the following:

from  typing import Optional

class MyClass:

    def Bfunction(self, city: Optional[str], name: Optional[str]):
        return city

    def Afunction(self):
        cities = self.Bfunction(name=None, city="ny", page='page')
        print(cities)
        cities = self.Bfunction(name="Tom", city=None, page='page')
        print(cities)

Note that in functions.py:

  1. I have defined a class because I have seen that your definition of the Bfunction contains the self argument.
  2. The Afunction executes the two calls of Bfunction that you have write in your question.

The test file (which doesn't use pytest but only the module unittest) is the following:

import unittest
from unittest import mock
import functions

class MyTestCase(unittest.TestCase):

    def test_something(self):
        sut = functions.MyClass()
        with mock.patch.object(sut, 'Bfunction') as mock_b:
            # set values returned by Bfunction in the first and in the second call
            mock_b.side_effect = ['cityA', 'cityB']
            sut.Afunction()
            # here I verify that Bfunction is called 2 times
            self.assertEqual(2, mock_b.call_count)
            mock_b.assert_called_with(city=None, name='Tom', page='page')
            mock_b.assert_called_with(name="Tom", city=None, page='page')


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

The output of the test file execution is:

cityA
cityB
.
----------------------------------------------------------------------
Ran 1 test in 0.002s

OK

The output showes:

  1. Afunction calls exactly two times the Bfunction
  2. the test verify the arguments passed to Bfunction
  3. the presence of cityA and cityB in the output show you how to select different values returned by Bfunction (you have to use side_effect and not return_value).

I hope that this answer is useful for you.

  • Related