python / python/cpython

Unittest Patch only works on attributes, methods

Open
#129,629 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Bug report

Bug description:

I am having an issue where the patch method from the unittest.mock module is only working when I patch attributes of imported modules in other files. I do not understand if this is an issue of my import patterns or a misunderstanding of how patching works. (I am importing the app code before the patch is able to run).

I devised a test to show how the functionality is confusing me:

In my app code I have the following code

    from datetime import date
    ...
    import boto3
    ...
    from common.functions import asodates2datetime
    from core.mixins.usecases import ASOUsecaseTools


    class foo:
        @staticmethod
        @ASOUsecaseTools.get_by_name
        def bar(self, relevant_date):
            client = boto3.client("s3")
            today = date.today()
            date = asodates2datetime(relevant_date)

And from my test code I can patch in the following way:

    @mock.patch("src.aoi.usecases.date")
    @mock.patch("src.aoi.usecases.ASOUsecaseTools.get_by_name")
    @mock.patch("src.aoi.usecases.asodates2datetime")
    @mock.patch("src.aoi.usecases.boto3.client")
    def test_foo_gets_bar(self, m_d, m_g, m_a, m_c):
        m_d.today.return_value = <date>
        m_d.side_effect = lambda *args, **kw: date(*args, **kw)
        # more patching of functions here

However, when the test is run, If I set a breakpoint in the bar function and check the value of the patched items, only the patched methods show up as MagicMock

    >>> boto3.client
        <MagicMock name='client...>
    >>> ASOUsecaseTools.get_by_name
        <MagicMock name='get_by_name...>
    >>> asodates2datetime
        <function asodates2datetime...>
    >>> date
        <class 'datetime.date'>

The thing that confuses me is that I am patching along with how the docs say to patch: https://docs.python.org/3/library/unittest.mock-examples.html#partial-mocking

Also the fact that patched methods work but classes and functions don't indicates that the import structure is not a problem because the imported methods would be overwritten in the same way as the imported classes. It also indicates that the import paths are not incorrect; as the only difference between the patches is the type of object they are patching.

My current workaround is using the freeze package to freeze the date, but I prefer using builtin packages if possible.

CPython versions tested on:

3.9

Operating systems tested on:

macOS

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 with the supplied unittest.mock reproducer in the issue and compare its patch targets with the linked partial-mocking documentation. Verify the behavior on CPython 3.9, then establish whether the observed difference is a reproducible interpreter bug or an import/patching misunderstanding; a concrete failing test and expected behavior would define done.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
testing-qa
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.