Rocksolid Light

News from da outaworlds

mail  files  register  groups  login

Message-ID:  

Q: What's a light-year? A: One-third less calories than a regular year.


comp / comp.lang.python / Trouble with mocking

SubjectAuthor
* Trouble with mockingNorman Robins
`- Re: Trouble with mockingMark Bourne

1
Subject: Trouble with mocking
From: Norman Robins
Newsgroups: comp.lang.python
Date: Wed, 4 Sep 2024 00:34 UTC
References: 1
Path: eternal-september.org!news.eternal-september.org!feeder3.eternal-september.org!fu-berlin.de!uni-berlin.de!not-for-mail
From: norman.robins59@gmail.com (Norman Robins)
Newsgroups: comp.lang.python
Subject: Trouble with mocking
Date: Tue, 3 Sep 2024 17:34:13 -0700
Lines: 47
Message-ID: <mailman.31.1725423711.2917.python-list@python.org>
References: <CAF2QC-nP2Sd3y7O+TXV75=BwR0kXr8iRR-P-z6m8OWD5F_q4Ug@mail.gmail.com>
Mime-Version: 1.0
Content-Type: text/plain; charset="UTF-8"
X-Trace: news.uni-berlin.de MhVSrZj1IxDn+ZR2ioyFCQLJrX2nxt+2aWGpWrv2ytVQ==
Cancel-Lock: sha1:Y/M6LfG3/fFZ15Nvx/Wn33Mqf2s= sha256:2JY/P8+4jsdxtYkuUlkx3LJGVSFoxVRNG1fpqnQWY2M=
Return-Path: <norman.robins59@gmail.com>
X-Original-To: python-list@python.org
Delivered-To: python-list@mail.python.org
Authentication-Results: mail.python.org; dkim=pass
reason="2048-bit key; unprotected key"
header.d=gmail.com header.i=@gmail.com header.b=Ky45jaJ/;
dkim-adsp=pass; dkim-atps=neutral
X-Spam-Status: OK 0.011
X-Spam-Evidence: '*H*': 0.98; '*S*': 0.00; 'this:': 0.03; 'def': 0.04;
'it"': 0.09; 'skip:\xc2 20': 0.09; 'import': 0.15; 'functions.':
0.16; 'somewhat': 0.16; 'subject:Trouble': 0.16; 'yield': 0.16;
'instead': 0.17; 'calls': 0.19; 'to:addr:python-list': 0.20;
'code': 0.23; 'skip:p 30': 0.23; 'thanks!': 0.24; 'function':
0.27; 'to:name:python': 0.32; 'message-id:@mail.gmail.com': 0.32;
"i'm": 0.33; 'someone': 0.34; 'received:google.com': 0.34;
'from:addr:gmail.com': 0.35; 'both': 0.40; 'want': 0.40; 'skip:m
20': 0.63; 'let': 0.66; 'skip:t 20': 0.66; 'skip:\xc2 40': 0.69;
'skip:f 20': 0.75; 'unit': 0.81
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=gmail.com; s=20230601; t=1725410065; x=1726014865; darn=python.org;
h=to:subject:message-id:date:from:mime-version:from:to:cc:subject
:date:message-id:reply-to;
bh=yTALGCdi3Gq1aNimVFiKAy0RdPCNrSOWKXu6RvUkp38=;
b=Ky45jaJ/tcmids/Qy6+2Kwv79JDOYM3uUtTnc4In/2Ff+gbMDBvVXDbDGXCTn1wBO7
OLBA3NqE+YdeqdVxRAbJuu0kUZQq6uMo7p4tI0Sn+WqtrPZ20lY9pTPrd0vHFNm1tqq1
Rxb+7vY3mO1AN9wW+doBC7t8dXI7YYGGucVLKPgiD94+vZ/svEcyEqNLBe3r+gmKi6Fr
dGVG/k3AHqQ2vm7EWhek53SQ5nCgf38Sv1F1Q//yBELj3m47flTTV91xKGBglfrfw7GU
lC9+4EQZ17DflPhbjVLvCpIVj9pVwpIQhlzB+yyBiiAE9SIyRQ+76NvYKltBVVr/MhyF
ztgQ==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=1e100.net; s=20230601; t=1725410065; x=1726014865;
h=to:subject:message-id:date:from:mime-version:x-gm-message-state
:from:to:cc:subject:date:message-id:reply-to;
bh=yTALGCdi3Gq1aNimVFiKAy0RdPCNrSOWKXu6RvUkp38=;
b=VO4MhMrBKX7RBBFDR7C+0vpPu5PZx5GCOgCgGIjcS5gqwZAhiOH7Eu9j9xTB1IOmo+
saKJybDu6ttTztPLWk+DpRbCq9Ku4hw3NsHZ5k51Mpuln9g13mrRmOPUosoe+P1h5mbM
i47TTb1lH2ANCyYZqXn9aDOwSm4Lot5iyhnFuPqWIRbQn4eoPzRMBwk98Jp+lduFXAw7
A+kI1msU2GF21C5zq5xTMri+OMw0Q3U23DdYU7dc+IXFzOunsQHSvwgiFRExO40KzMIj
wm3zSeZ/+if2t3zAWfQGEIxhfmfrfbqBu7tTyp88ztreRs2MPuTO/Q5jGENPSI5F1cUr
1s1g==
X-Gm-Message-State: AOJu0YypHxWS5BReLsbSiaGdcLDyu8/x1gCwlDLMZPkBahduQHTuDe1W
cBbbZllfjDJLflgxnOuPCaykMBNcB3Q1z3Wf8qE+knB39plDVe5bBFnlC0SSI7/XV4Kb3YayA12
BBtSc7PULfc62VMLUPsK+lNkV13HDW/02
X-Google-Smtp-Source: AGHT+IHGaxCEMZYjLDTn+TbzFCgpXNqecYCkFQkpW4s7LueYzzzcosVgYFP9MJ3Jx9FgXL4cIhwm2AdcukaSXTNR5UA=
X-Received: by 2002:a17:907:e8d:b0:a86:899d:6459 with SMTP id
a640c23a62f3a-a8a32eda727mr171694866b.38.1725410064445; Tue, 03 Sep 2024
17:34:24 -0700 (PDT)
X-Mailman-Approved-At: Wed, 04 Sep 2024 00:21:50 -0400
X-Content-Filtered-By: Mailman/MimeDel 2.1.39
X-BeenThere: python-list@python.org
X-Mailman-Version: 2.1.39
Precedence: list
List-Id: General discussion list for the Python programming language
<python-list.python.org>
List-Unsubscribe: <https://mail.python.org/mailman/options/python-list>,
<mailto:python-list-request@python.org?subject=unsubscribe>
List-Archive: <https://mail.python.org/pipermail/python-list/>
List-Post: <mailto:python-list@python.org>
List-Help: <mailto:python-list-request@python.org?subject=help>
List-Subscribe: <https://mail.python.org/mailman/listinfo/python-list>,
<mailto:python-list-request@python.org?subject=subscribe>
X-Mailman-Original-Message-ID: <CAF2QC-nP2Sd3y7O+TXV75=BwR0kXr8iRR-P-z6m8OWD5F_q4Ug@mail.gmail.com>
View all headers

I'm somewhat new to mocking for unit tests.

I have some code like this:

In foo/bar/baz.py I have 2 function I want to mock, one calls the other"
def function1_to_mock():
.
.
.

def function2_to_mock():
function1_to_mock()

In foo/bar/main.py I import 1 of these and call it"
from .baz import function2_to_mock

def some_function():
function1_to_mock()
.
.
.

I want to mock both function1_to_mock and function2_to_mock

In my test I do this:

def function1_to_mock(kid):
return MOCKED_VALUE

@pytest.fixture(autouse=True)
def mock_dependencies():
with patch(foo.bar.baz.function1_to_mock') as mock_function1_to_mock, \
patch('foo.bar.main.function2_to_mock') as mock_function2_to_mock:
mock_function2_to_mock.return_value = {
'this': 'that
}
yield mock_function1_to_mock, mock_function2_to_mock

def test_main(mock_dependencies):
some_function()

When some_function is called the real function1_to_mock is called instead
of my mock.

Can someone please let me know how to properly mock these 2 functions.

Thanks!

Subject: Re: Trouble with mocking
From: Mark Bourne
Newsgroups: comp.lang.python
Organization: A noiseless patient Spider
Date: Fri, 20 Sep 2024 19:00 UTC
References: 1 2
Path: eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail
From: nntp.mbourne@spamgourmet.com (Mark Bourne)
Newsgroups: comp.lang.python
Subject: Re: Trouble with mocking
Date: Fri, 20 Sep 2024 20:00:57 +0100
Organization: A noiseless patient Spider
Lines: 108
Message-ID: <vckgpb$17fg2$1@dont-email.me>
References: <CAF2QC-nP2Sd3y7O+TXV75=BwR0kXr8iRR-P-z6m8OWD5F_q4Ug@mail.gmail.com>
<mailman.31.1725423711.2917.python-list@python.org>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
Injection-Date: Fri, 20 Sep 2024 21:00:59 +0200 (CEST)
Injection-Info: dont-email.me; posting-host="796ec347a7da2436ce12268c7d8171ce";
logging-data="1293826"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1+c8ccdGfROfHJ1koHgzFqq"
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101
SeaMonkey/2.53.18.2
Cancel-Lock: sha1:gcCPpBIy/Lb7aT2rPjlx3bG1iVU=
In-Reply-To: <mailman.31.1725423711.2917.python-list@python.org>
View all headers

Norman Robins wrote:
> I'm somewhat new to mocking for unit tests.
>
> I have some code like this:
>
> In foo/bar/baz.py I have 2 function I want to mock, one calls the other"
> def function1_to_mock():
> .
> .
> .
>
> def function2_to_mock():
> function1_to_mock()
>
> In foo/bar/main.py I import 1 of these and call it"
> from .baz import function2_to_mock
>
> def some_function():
> function1_to_mock()

I'm assuming this is supposed to be calling `function2_to_mock`?
(Otherwise the import should be for `function1_to_mock`, but then the
fact `function2_to_mock` also calls `function1_to_mock` would be irrelevant)

> .
> .
> .
>
> I want to mock both function1_to_mock and function2_to_mock
>
> In my test I do this:
>
> def function1_to_mock(kid):
> return MOCKED_VALUE
>
> @pytest.fixture(autouse=True)
> def mock_dependencies():
> with patch(foo.bar.baz.function1_to_mock') as mock_function1_to_mock, \
> patch('foo.bar.main.function2_to_mock') as mock_function2_to_mock:
> mock_function2_to_mock.return_value = {
> 'this': 'that
> }
> yield mock_function1_to_mock, mock_function2_to_mock
>
> def test_main(mock_dependencies):
> some_function()
>
> When some_function is called the real function1_to_mock is called instead
> of my mock.
>
> Can someone please let me know how to properly mock these 2 functions.
>
> Thanks!

In `foo/bar/main.py`, the line:
```
from .baz import function2_to_mock
```
creates a reference named `function2_to_mock` in `main.py` referring to
the method in `foo/bar/baz.py`.

When you patch `foo.bar.baz.function2_to_mock`, you're patching the
reference in `foo.bar.baz`, but the reference in `foo.bar.main` still
refers to the original function object.

There are at least a couple of ways around this. The way I prefer is to
change `main.py` to import the `baz` module rather than just the function:
```
> from . import baz
>
> def some_function():
> baz.function2_to_mock()
```
Here, `main.py` has a reference to the `baz` module rather than the
individual function object. It looks up `function2_to_mock` in `baz`
just before calling it so, when the `baz` module is patched so that
`baz.function2_to_mock` refers to a mock, the call in main.py` gets the
mock and calls that rather than the original function.

There no memory saving by importing just the functions you need from
`baz` - the whole module is still loaded on import and remains in
memory, it's just that `main` only gets a reference to the one function.
The effect is similar to doing something like:
```
from . import baz
function2_to_mock = baz.function2_to_mock
del baz
```
....including the fact that, after the import, the reference to
`function2_to_mock` in `main` is just a copy of the reference in `baz`,
hence not getting updated by the patch.

The other way around it is to patch `main.function2_to_mock` instead of
patching `foo.bar.baz.function2_to_mock`.

See also the documentation under "where to patch" at
<https://docs.python.org/3/library/unittest.mock.html#where-to-patch>.

Note that, since you're patching `function2_to_mock`, you don't
necessarily need to patch `function1_to_mock` as well. The mock of
`function2_to_mock` won't call `function1_to_mock` (or its mock)
regardless of whether `function1_to_mock` has been patched, unless you
set the mock of `function2_to_mock` to do so. You don't necessarily
need to patch `function1_to_mock`, unless of course there are other
calls to it that you need to mock.

--
Mark.

1

rocksolid light 0.9.8
clearnet tor