'How do I mock a function with no parameters that sets a class attribute for Python unit tests?

My class, Foo, has a calculate method that takes no parameters, makes a call to a database and does some calculations using class attributes, and sets another class attribute, calculation_results (which is a dict).

I'm trying to mock a separate function that includes a call to Foo.calculate().

I tried something like the below, where mock_calculate was a function defined in my test class:

def mock_calculate():
    pass

@mock.patch("path.Foo.validate", new=mock_calculate)
@mock.patch("path.Foo")
def test_func_that_calls_foo(self, mock_foo):
    mock_foo.return_value.calculation_results = {first_calc : 1, second_calc: 2, database_mismatches : []}
    func_that_calls_foo(#TEST_INPUT)
    #assertions

This code does set the calculation_result attribute, but it still calls the actual calculate function, which fails when it tries to hit the database. How can I set Foo.calculation_results directly and also not actually call calculate()? The fact that calculate() doesn't take params or return anything is making it tough to follow most examples I see of mocking--otherwise I could just set a return_value or, inside the function, set the attribute based on the params passed.



Solution 1:[1]

You can simply also patch the method calculate, and then the real implementation will not be called.

I might implement this differently though, using a lazy operator and a PropertyMock. Code sample:

from unittest import mock


class Foo:
    def __init__(self):
        self._calculated_results = {}

    @property
    def calculated_results(self):
        if not self._calculated_results:
            self._calculated_results = self.calculate()
        return self._calculated_results

    def do_something_with_results(self):
        return self.calculated_results

    def calculate(self):
        assert False, 'Ensure this method is not called, since it would call the database'
        return {}


@mock.patch.object(Foo, 'calculated_results', new_callable=mock.PropertyMock, return_value={'key': 'value'})
def test_func_that_calls_foo(mock_foo):
    x = Foo().do_something_with_results()
    assert x == {'key': 'value'}


test_func_that_calls_foo()

This test function passes with the patch, and fails without the patch: AssertionError: Ensure this method is not called, since it would call the database.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 physicalattraction