python / python/cpython

side_effect function of PropertyMock gets called only once

Open
#94,323 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

docs stdlib type-bug
Dominant language
Python
Stars
77.2k
Forks
36k
PR merge metrics
PR metrics pending

Description

Bug report

Expected behavior

The side_effect function of a PropertyMock should work like the side_effect function of other Mock-Objects

Seen behavior

side_effect function of PropertyMock is called only once instead of twice in below example.

Minimal example

File dut.py:

class Class1():
    def __init__(self, name):
        self.__name = name

    @property
    def name(self):
        return self.__name

    def name_func(self):
        return self.__name


class Class2():
    def __init__(self, name, class1):
        self.__name = name
        self.__class1 = class1

    @property
    def name(self):
        return self.__name
    @property
    def class1(self):
        return self.__class1

File test\test_dut.py:

import dut
import unittest
from unittest.mock import patch, PropertyMock

class TestClass2(unittest.TestCase):
    def test_func(self):
        side_effect_counter = -1
        def side_effect_func(_):
            nonlocal side_effect_counter
            side_effect_counter += 1
            return f'text_{side_effect_counter}'

        c2_1 = dut.Class2('class2',  dut.Class1('class1'))
        c2_2 = dut.Class2('class2_2', dut.Class1('class1_2'))
        with patch('test_dut.dut.Class1.name_func', side_effect=side_effect_func, autospec=True):
            print(f'{c2_2.class1.name_func()}, {c2_1.class1.name_func()}')

    def test_prop(self):
        side_effect_counter = -1
        def side_effect_func():
            nonlocal side_effect_counter
            side_effect_counter += 1
            return f'text_{side_effect_counter}'

        c2_1 = dut.Class2('class2',  dut.Class1('class1'))
        c2_2 = dut.Class2('class2_2', dut.Class1('class1_2'))
        with patch.object(dut.Class1, 'name', new_callable=PropertyMock(side_effect=side_effect_func)):
            print(f'{c2_2.class1.name}, {c2_1.class1.name}')

Call from command line: pytest -rP test\test_dut.py

This produces the following output (problematic line marked by me):

============================================================================================== test session starts ==============================================================================================
platform win32 -- Python 3.9.12, pytest-7.1.2, pluggy-1.0.0
rootdir: C:\Users\klosemic\Documents\playground_mocks
plugins: hypothesis-6.46.5, cov-3.0.0, forked-1.4.0, html-3.1.1, metadata-2.0.1, xdist-2.5.0
collected 2 items

test\test_dut.py ..                                                                                                                                                                                        [100%]

==================================================================================================== PASSES =====================================================================================================
_____________________________________________________________________________________________ TestClass2.test_func ______________________________________________________________________________________________
--------------------------------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------------------------------
text_0, text_1
_____________________________________________________________________________________________ TestClass2.test_prop ______________________________________________________________________________________________
--------------------------------------------------------------------------------------------- Captured stdout call ----------------------------------------------------------------------------------------------
text_0, text_0 <<<<<< HERE IS THE PROBLEM
=============================================================================================== 2 passed in 0.46s ===============================================================================================

Your environment
Windows 10, Python 3.9.12.
More version information can be found in the output of example above

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the issue with the provided dut.py and test/test_dut.py example, comparing PropertyMock with the working Mock side_effect case. Trace the unittest.mock PropertyMock behavior and add a regression test showing that the side_effect function runs for each property access, then run the supplied pytest command.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
testing-qa
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.