“Python/docs/3.9/library/unittest.mock-examples”的版本间差异

来自菜鸟教程
Python/docs/3.9/library/unittest.mock-examples
跳转至:导航、​搜索
(autoload)
 
(Page commit)
 
第1行: 第1行:
 +
{{DISPLAYTITLE:unittest.mock — 入门 — Python 文档}}
 
<div id="unittest-mock-getting-started" class="section">
 
<div id="unittest-mock-getting-started" class="section">
  
= [[../unittest.mock#module-unittest|<code>unittest.mock</code>]] --- getting started =
+
= unittest.mock — 入门 =
  
 
<div class="versionadded">
 
<div class="versionadded">
  
<span class="versionmodified added">3.3 新版功能.</span>
+
<span class="versionmodified added">3.3 版中的新功能。</span>
  
  
第12行: 第13行:
 
<div id="using-mock" class="section">
 
<div id="using-mock" class="section">
  
== Using Mock ==
+
== 使用模拟 ==
  
 
<div id="mock-patching-methods" class="section">
 
<div id="mock-patching-methods" class="section">
  
=== Mock Patching Methods ===
+
=== 模拟修补方法 ===
  
Common uses for [[../unittest.mock#unittest.mock|<code>Mock</code>]] objects include:
+
[[../unittest.mock#unittest.mock|Mock]] 对象的常见用途包括:
  
* Patching methods
+
* 修补方法
* Recording method calls on objects
+
* 记录对象的方法调用
  
You might want to replace a method on an object to check that
+
您可能想要替换对象上的方法以检查系统的另一部分是否使用正确的参数调用它:
it is called with the correct arguments by another part of the system:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第30行: 第30行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; real = SomeClass()
+
<syntaxhighlight lang="python">>>> real = SomeClass()
&gt;&gt;&gt; real.method = MagicMock(name='method')
+
>>> real.method = MagicMock(name='method')
&gt;&gt;&gt; real.method(3, 4, 5, key='value')
+
>>> real.method(3, 4, 5, key='value')
&lt;MagicMock name='method()' id='...'&gt;</pre>
+
<MagicMock name='method()' id='...'></syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Once our mock has been used (<code>real.method</code> in this example) it has methods
+
一旦使用了我们的模拟(在本例中为 <code>real.method</code>),它就会有一些方法和属性,可以让你断言它是如何被使用的。
and attributes that allow you to make assertions about how it has been used.
 
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
In most of these examples the [[../unittest.mock#unittest.mock|<code>Mock</code>]] and [[../unittest.mock#unittest.mock|<code>MagicMock</code>]] classes
+
在大多数这些示例中,[[../unittest.mock#unittest.mock|Mock]] [[../unittest.mock#unittest.mock|MagicMock]] 类是可以互换的。 由于 <code>MagicMock</code> 是功能更强大的类,因此默认情况下使用它是一个明智的选择。
are interchangeable. As the <code>MagicMock</code> is the more capable class it makes
 
a sensible one to use by default.
 
  
  
 
</div>
 
</div>
Once the mock has been called its [[../unittest.mock#unittest.mock.Mock|<code>called</code>]] attribute is set to
+
一旦模拟被调用,它的 [[../unittest.mock#unittest.mock.Mock|called]] 属性被设置为 <code>True</code>。 更重要的是,我们可以使用 [[../unittest.mock#unittest.mock.Mock|assert_called_with()]] [[../unittest.mock#unittest.mock.Mock|assert_Called_once_with()]] 方法来检查它是否使用正确的参数被调用。
<code>True</code>. More importantly we can use the [[../unittest.mock#unittest.mock.Mock|<code>assert_called_with()</code>]] or
 
[[../unittest.mock#unittest.mock.Mock|<code>assert_called_once_with()</code>]] method to check that it was called with
 
the correct arguments.
 
  
This example tests that calling <code>ProductionClass().method</code> results in a call to
+
此示例测试调用 <code>ProductionClass().method</code> 会导致调用 <code>something</code> 方法:
the <code>something</code> method:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第63行: 第56行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class ProductionClass:
+
<syntaxhighlight lang="python">>>> class ProductionClass:
 
...    def method(self):
 
...    def method(self):
 
...        self.something(1, 2, 3)
 
...        self.something(1, 2, 3)
第69行: 第62行:
 
...        pass
 
...        pass
 
...
 
...
&gt;&gt;&gt; real = ProductionClass()
+
>>> real = ProductionClass()
&gt;&gt;&gt; real.something = MagicMock()
+
>>> real.something = MagicMock()
&gt;&gt;&gt; real.method()
+
>>> real.method()
&gt;&gt;&gt; real.something.assert_called_once_with(1, 2, 3)</pre>
+
>>> real.something.assert_called_once_with(1, 2, 3)</syntaxhighlight>
  
 
</div>
 
</div>
第81行: 第74行:
 
<div id="mock-for-method-calls-on-an-object" class="section">
 
<div id="mock-for-method-calls-on-an-object" class="section">
  
=== Mock for Method Calls on an Object ===
+
=== 模拟对象上的方法调用 ===
  
In the last example we patched a method directly on an object to check that it
+
在上一个示例中,我们直接在对象上修补了一个方法以检查它是否被正确调用。 另一个常见的用例是将一个对象传递给一个方法(或被测系统的某个部分),然后检查它是否以正确的方式使用。
was called correctly. Another common use case is to pass an object into a
 
method (or some part of the system under test) and then check that it is used
 
in the correct way.
 
  
The simple <code>ProductionClass</code> below has a <code>closer</code> method. If it is called with
+
下面简单的<code>ProductionClass</code>有一个<code>closer</code>方法。 如果它被一个对象调用,那么它会在它上面调用 <code>close</code>
an object then it calls <code>close</code> on it.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第95行: 第84行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class ProductionClass:
+
<syntaxhighlight lang="python">>>> class ProductionClass:
 
...    def closer(self, something):
 
...    def closer(self, something):
 
...        something.close()
 
...        something.close()
...</pre>
+
...</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
So to test it we need to pass in an object with a <code>close</code> method and check
+
所以为了测试它,我们需要使用 <code>close</code> 方法传入一个对象并检查它是否被正确调用。
that it was called correctly.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第110行: 第98行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; real = ProductionClass()
+
<syntaxhighlight lang="python">>>> real = ProductionClass()
&gt;&gt;&gt; mock = Mock()
+
>>> mock = Mock()
&gt;&gt;&gt; real.closer(mock)
+
>>> real.closer(mock)
&gt;&gt;&gt; mock.close.assert_called_with()</pre>
+
>>> mock.close.assert_called_with()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
We don't have to do any work to provide the 'close' method on our mock.
+
我们不需要做任何工作来在我们的模拟上提供 'close' 方法。 访问 close 创建它。 因此,如果 'close' 尚未被调用,则在测试中访问它会创建它,但 [[../unittest.mock#unittest.mock.Mock|assert_Called_with()]] 将引发失败异常。
Accessing close creates it. So, if 'close' hasn't already been called then
 
accessing it in the test will create it, but [[../unittest.mock#unittest.mock.Mock|<code>assert_called_with()</code>]]
 
will raise a failure exception.
 
  
  
第127行: 第112行:
 
<div id="mocking-classes" class="section">
 
<div id="mocking-classes" class="section">
  
=== Mocking Classes ===
+
=== 模拟类 ===
  
A common use case is to mock out classes instantiated by your code under test.
+
一个常见的用例是模拟由您的被测代码实例化的类。 当您修补一个类时,该类将被替换为模拟。 实例是通过 ''调用类'' 创建的。 这意味着您可以通过查看模拟类的返回值来访问“模拟实例”。
When you patch a class, then that class is replaced with a mock. Instances
 
are created by ''calling the class''. This means you access the &quot;mock instance&quot;
 
by looking at the return value of the mocked class.
 
  
In the example below we have a function <code>some_function</code> that instantiates <code>Foo</code>
+
在下面的例子中,我们有一个函数 <code>some_function</code> 实例化 <code>Foo</code> 并调用它的方法。 对 [[../unittest.mock#unittest.mock|patch()]] 的调用用模拟替换了类 <code>Foo</code><code>Foo</code>实例是调用mock的结果,所以通过修改mock[[../unittest.mock#unittest.mock.Mock|return_value]]来配置。
and calls a method on it. The call to [[../unittest.mock#unittest.mock|<code>patch()</code>]] replaces the class <code>Foo</code> with a
 
mock. The <code>Foo</code> instance is the result of calling the mock, so it is configured
 
by modifying the mock [[../unittest.mock#unittest.mock.Mock|<code>return_value</code>]].
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第143行: 第122行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; def some_function():
+
<syntaxhighlight lang="python3">>>> def some_function():
 
...    instance = module.Foo()
 
...    instance = module.Foo()
 
...    return instance.method()
 
...    return instance.method()
 
...
 
...
&gt;&gt;&gt; with patch('module.Foo') as mock:
+
>>> with patch('module.Foo') as mock:
 
...    instance = mock.return_value
 
...    instance = mock.return_value
 
...    instance.method.return_value = 'the result'
 
...    instance.method.return_value = 'the result'
 
...    result = some_function()
 
...    result = some_function()
...    assert result == 'the result'</pre>
+
...    assert result == 'the result'</syntaxhighlight>
  
 
</div>
 
</div>
第160行: 第139行:
 
<div id="naming-your-mocks" class="section">
 
<div id="naming-your-mocks" class="section">
  
=== Naming your mocks ===
+
=== 命名你的模拟 ===
  
It can be useful to give your mocks a name. The name is shown in the repr of
+
给你的模拟一个名字会很有用。 该名称显示在模拟的 repr 中,当模拟出现在测试失败消息中时会很有帮助。 该名称还传播到模拟的属性或方法:
the mock and can be helpful when the mock appears in test failure messages. The
 
name is also propagated to attributes or methods of the mock:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第170行: 第147行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = MagicMock(name='foo')
+
<syntaxhighlight lang="python">>>> mock = MagicMock(name='foo')
&gt;&gt;&gt; mock
+
>>> mock
&lt;MagicMock name='foo' id='...'&gt;
+
<MagicMock name='foo' id='...'>
&gt;&gt;&gt; mock.method
+
>>> mock.method
&lt;MagicMock name='foo.method' id='...'&gt;</pre>
+
<MagicMock name='foo.method' id='...'></syntaxhighlight>
  
 
</div>
 
</div>
第183行: 第160行:
 
<div id="tracking-all-calls" class="section">
 
<div id="tracking-all-calls" class="section">
  
=== Tracking all Calls ===
+
=== 跟踪所有呼叫 ===
  
Often you want to track more than a single call to a method. The
+
通常,您希望跟踪对方法的多次调用。 [[../unittest.mock#unittest.mock.Mock|mock_calls]] 属性记录了对模拟的子属性的所有调用——以及对它们的子属性的调用。
[[../unittest.mock#unittest.mock.Mock|<code>mock_calls</code>]] attribute records all calls
 
to child attributes of the mock - and also to their children.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第193行: 第168行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = MagicMock()
+
<syntaxhighlight lang="python">>>> mock = MagicMock()
&gt;&gt;&gt; mock.method()
+
>>> mock.method()
&lt;MagicMock name='mock.method()' id='...'&gt;
+
<MagicMock name='mock.method()' id='...'>
&gt;&gt;&gt; mock.attribute.method(10, x=53)
+
>>> mock.attribute.method(10, x=53)
&lt;MagicMock name='mock.attribute.method()' id='...'&gt;
+
<MagicMock name='mock.attribute.method()' id='...'>
&gt;&gt;&gt; mock.mock_calls
+
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]</pre>
+
[call.method(), call.attribute.method(10, x=53)]</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If you make an assertion about <code>mock_calls</code> and any unexpected methods
+
如果您对 <code>mock_calls</code> 进行断言并且调用了任何意外方法,则该断言将失败。 这很有用,因为除了断言您期望的调用已经进行之外,您还可以检查它们是否以正确的顺序进行并且没有额外的调用:
have been called, then the assertion will fail. This is useful because as well
 
as asserting that the calls you expected have been made, you are also checking
 
that they were made in the right order and with no additional calls:
 
  
You use the [[../unittest.mock#unittest.mock|<code>call</code>]] object to construct lists for comparing with
+
您使用 [[../unittest.mock#unittest.mock|call]] 对象来构造列表以与 <code>mock_calls</code> 进行比较:
<code>mock_calls</code>:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第216行: 第187行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; expected = [call.method(), call.attribute.method(10, x=53)]
+
<syntaxhighlight lang="python">>>> expected = [call.method(), call.attribute.method(10, x=53)]
&gt;&gt;&gt; mock.mock_calls == expected
+
>>> mock.mock_calls == expected
True</pre>
+
True</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
However, parameters to calls that return mocks are not recorded, which means it is not
+
但是,不会记录返回模拟的调用的参数,这意味着无法跟踪用于创建祖先的参数很重要的嵌套调用:
possible to track nested calls where the parameters used to create ancestors are important:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第230行: 第200行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; m = Mock()
+
<syntaxhighlight lang="python">>>> m = Mock()
&gt;&gt;&gt; m.factory(important=True).deliver()
+
>>> m.factory(important=True).deliver()
&lt;Mock name='mock.factory().deliver()' id='...'&gt;
+
<Mock name='mock.factory().deliver()' id='...'>
&gt;&gt;&gt; m.mock_calls[-1] == call.factory(important=False).deliver()
+
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True</pre>
+
True</syntaxhighlight>
  
 
</div>
 
</div>
第243行: 第213行:
 
<div id="setting-return-values-and-attributes" class="section">
 
<div id="setting-return-values-and-attributes" class="section">
  
=== Setting Return Values and Attributes ===
+
=== 设置返回值和属性 ===
  
Setting the return values on a mock object is trivially easy:
+
在模拟对象上设置返回值非常简单:
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第251行: 第221行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock()
+
<syntaxhighlight lang="python">>>> mock = Mock()
&gt;&gt;&gt; mock.return_value = 3
+
>>> mock.return_value = 3
&gt;&gt;&gt; mock()
+
>>> mock()
3</pre>
+
3</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Of course you can do the same for methods on the mock:
+
当然,您可以对模拟上的方法执行相同的操作:
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第265行: 第235行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock()
+
<syntaxhighlight lang="python">>>> mock = Mock()
&gt;&gt;&gt; mock.method.return_value = 3
+
>>> mock.method.return_value = 3
&gt;&gt;&gt; mock.method()
+
>>> mock.method()
3</pre>
+
3</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The return value can also be set in the constructor:
+
返回值也可以在构造函数中设置:
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第279行: 第249行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock(return_value=3)
+
<syntaxhighlight lang="python">>>> mock = Mock(return_value=3)
&gt;&gt;&gt; mock()
+
>>> mock()
3</pre>
+
3</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If you need an attribute setting on your mock, just do it:
+
如果您需要在模拟上设置属性,请执行以下操作:
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第292行: 第262行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock()
+
<syntaxhighlight lang="python">>>> mock = Mock()
&gt;&gt;&gt; mock.x = 3
+
>>> mock.x = 3
&gt;&gt;&gt; mock.x
+
>>> mock.x
3</pre>
+
3</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Sometimes you want to mock up a more complex situation, like for example
+
有时您想模拟更复杂的情况,例如 <code>mock.connection.cursor().execute(&quot;SELECT 1&quot;)</code>。 如果我们希望这个调用返回一个列表,那么我们必须配置嵌套调用的结果。
<code>mock.connection.cursor().execute(&quot;SELECT 1&quot;)</code>. If we wanted this call to
 
return a list, then we have to configure the result of the nested call.
 
  
We can use [[../unittest.mock#unittest.mock|<code>call</code>]] to construct the set of calls in a &quot;chained call&quot; like
+
我们可以使用 [[../unittest.mock#unittest.mock|call]] 在像这样的“链式调用”中构造调用集,以便之后轻松断言:
this for easy assertion afterwards:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第311行: 第278行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock()
+
<syntaxhighlight lang="python">>>> mock = Mock()
&gt;&gt;&gt; cursor = mock.connection.cursor.return_value
+
>>> cursor = mock.connection.cursor.return_value
&gt;&gt;&gt; cursor.execute.return_value = ['foo']
+
>>> cursor.execute.return_value = ['foo']
&gt;&gt;&gt; mock.connection.cursor().execute(&quot;SELECT 1&quot;)
+
>>> mock.connection.cursor().execute("SELECT 1")
 
['foo']
 
['foo']
&gt;&gt;&gt; expected = call.connection.cursor().execute(&quot;SELECT 1&quot;).call_list()
+
>>> expected = call.connection.cursor().execute("SELECT 1").call_list()
&gt;&gt;&gt; mock.mock_calls
+
>>> mock.mock_calls
 
[call.connection.cursor(), call.connection.cursor().execute('SELECT 1')]
 
[call.connection.cursor(), call.connection.cursor().execute('SELECT 1')]
&gt;&gt;&gt; mock.mock_calls == expected
+
>>> mock.mock_calls == expected
True</pre>
+
True</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
It is the call to <code>.call_list()</code> that turns our call object into a list of
+
正是对 <code>.call_list()</code> 的调用将我们的调用对象转换为代表链接调用的调用列表。
calls representing the chained calls.
 
  
  
第332行: 第298行:
 
<div id="raising-exceptions-with-mocks" class="section">
 
<div id="raising-exceptions-with-mocks" class="section">
  
=== Raising exceptions with mocks ===
+
=== 使用模拟引发异常 ===
  
A useful attribute is [[../unittest.mock#unittest.mock.Mock|<code>side_effect</code>]]. If you set this to an
+
一个有用的属性是 [[../unittest.mock#unittest.mock.Mock|side_effect]]。 如果将此设置为异常类或实例,则在调用模拟时将引发异常。
exception class or instance then the exception will be raised when the mock
 
is called.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第342行: 第306行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock(side_effect=Exception('Boom!'))
+
<syntaxhighlight lang="python">>>> mock = Mock(side_effect=Exception('Boom!'))
&gt;&gt;&gt; mock()
+
>>> mock()
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
   ...
 
   ...
Exception: Boom!</pre>
+
Exception: Boom!</syntaxhighlight>
  
 
</div>
 
</div>
第355行: 第319行:
 
<div id="side-effect-functions-and-iterables" class="section">
 
<div id="side-effect-functions-and-iterables" class="section">
  
=== Side effect functions and iterables ===
+
=== 副作用函数和迭代 ===
  
<code>side_effect</code> can also be set to a function or an iterable. The use case for
+
<code>side_effect</code> 也可以设置为一个函数或一个可迭代对象。 <code>side_effect</code> 作为可迭代对象的用例是您的模拟将被多次调用,并且您希望每次调用都返回不同的值。 当您将 <code>side_effect</code> 设置为可迭代对象时,每次调用模拟都会返回可迭代对象的下一个值:
<code>side_effect</code> as an iterable is where your mock is going to be called several
 
times, and you want each call to return a different value. When you set
 
<code>side_effect</code> to an iterable every call to the mock returns the next value
 
from the iterable:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第367行: 第327行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = MagicMock(side_effect=[4, 5, 6])
+
<syntaxhighlight lang="python">>>> mock = MagicMock(side_effect=[4, 5, 6])
&gt;&gt;&gt; mock()
+
>>> mock()
 
4
 
4
&gt;&gt;&gt; mock()
+
>>> mock()
 
5
 
5
&gt;&gt;&gt; mock()
+
>>> mock()
6</pre>
+
6</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
For more advanced use cases, like dynamically varying the return values
+
对于更高级的用例,例如根据调用模拟的内容动态改变返回值,<code>side_effect</code> 可以是一个函数。 将使用与模拟相同的参数调用该函数。 无论函数返回什么,调用都会返回什么:
depending on what the mock is called with, <code>side_effect</code> can be a function.
 
The function will be called with the same arguments as the mock. Whatever the
 
function returns is what the call returns:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第387行: 第344行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; vals = {(1, 2): 1, (2, 3): 2}
+
<syntaxhighlight lang="python">>>> vals = {(1, 2): 1, (2, 3): 2}
&gt;&gt;&gt; def side_effect(*args):
+
>>> def side_effect(*args):
 
...    return vals[args]
 
...    return vals[args]
 
...
 
...
&gt;&gt;&gt; mock = MagicMock(side_effect=side_effect)
+
>>> mock = MagicMock(side_effect=side_effect)
&gt;&gt;&gt; mock(1, 2)
+
>>> mock(1, 2)
 
1
 
1
&gt;&gt;&gt; mock(2, 3)
+
>>> mock(2, 3)
2</pre>
+
2</syntaxhighlight>
  
 
</div>
 
</div>
第404行: 第361行:
 
<div id="mocking-asynchronous-iterators" class="section">
 
<div id="mocking-asynchronous-iterators" class="section">
  
=== Mocking asynchronous iterators ===
+
=== 模拟异步迭代器 ===
  
Since Python 3.8, <code>AsyncMock</code> and <code>MagicMock</code> have support to mock
+
Python 3.8 开始,<code>AsyncMock</code> <code>MagicMock</code> 支持通过 <code>__aiter__</code> 模拟 [[../../reference/datamodel#async-iterators|Asynchronous Iterators]]<code>__aiter__</code> [[../unittest.mock#unittest.mock.Mock|return_value]] 属性可用于设置要用于迭代的返回值。
[[../../reference/datamodel#async-iterators|<span class="std std-ref">Asynchronous Iterators</span>]] through <code>__aiter__</code>. The [[../unittest.mock#unittest.mock.Mock|<code>return_value</code>]]
 
attribute of <code>__aiter__</code> can be used to set the return values to be used for
 
iteration.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第415行: 第369行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = MagicMock()  # AsyncMock also works here
+
<syntaxhighlight lang="python">>>> mock = MagicMock()  # AsyncMock also works here
&gt;&gt;&gt; mock.__aiter__.return_value = [1, 2, 3]
+
>>> mock.__aiter__.return_value = [1, 2, 3]
&gt;&gt;&gt; async def main():
+
>>> async def main():
 
...    return [i async for i in mock]
 
...    return [i async for i in mock]
 
...
 
...
&gt;&gt;&gt; asyncio.run(main())
+
>>> asyncio.run(main())
[1, 2, 3]</pre>
+
[1, 2, 3]</syntaxhighlight>
  
 
</div>
 
</div>
第430行: 第384行:
 
<div id="mocking-asynchronous-context-manager" class="section">
 
<div id="mocking-asynchronous-context-manager" class="section">
  
=== Mocking asynchronous context manager ===
+
=== 模拟异步上下文管理器 ===
  
Since Python 3.8, <code>AsyncMock</code> and <code>MagicMock</code> have support to mock
+
Python 3.8 开始,<code>AsyncMock</code> <code>MagicMock</code> 支持通过 <code>__aenter__</code> 和 <code>__aexit__</code> 模拟 [[../../reference/datamodel#async-context-managers|异步上下文管理器]]。 默认情况下,<code>__aenter__</code> <code>__aexit__</code> 是返回异步函数的 <code>AsyncMock</code> 实例。
[[../../reference/datamodel#async-context-managers|<span class="std std-ref">Asynchronous Context Managers</span>]] through <code>__aenter__</code> and <code>__aexit__</code>.
 
By default, <code>__aenter__</code> and <code>__aexit__</code> are <code>AsyncMock</code> instances that
 
return an async function.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第441行: 第392行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class AsyncContextManager:
+
<syntaxhighlight lang="python">>>> class AsyncContextManager:
 
...    async def __aenter__(self):
 
...    async def __aenter__(self):
 
...        return self
 
...        return self
第447行: 第398行:
 
...        pass
 
...        pass
 
...
 
...
&gt;&gt;&gt; mock_instance = MagicMock(AsyncContextManager())  # AsyncMock also works here
+
>>> mock_instance = MagicMock(AsyncContextManager())  # AsyncMock also works here
&gt;&gt;&gt; async def main():
+
>>> async def main():
 
...    async with mock_instance as result:
 
...    async with mock_instance as result:
 
...        pass
 
...        pass
 
...
 
...
&gt;&gt;&gt; asyncio.run(main())
+
>>> asyncio.run(main())
&gt;&gt;&gt; mock_instance.__aenter__.assert_awaited_once()
+
>>> mock_instance.__aenter__.assert_awaited_once()
&gt;&gt;&gt; mock_instance.__aexit__.assert_awaited_once()</pre>
+
>>> mock_instance.__aexit__.assert_awaited_once()</syntaxhighlight>
  
 
</div>
 
</div>
第463行: 第414行:
 
<div id="creating-a-mock-from-an-existing-object" class="section">
 
<div id="creating-a-mock-from-an-existing-object" class="section">
  
=== Creating a Mock from an Existing Object ===
+
=== 从现有对象创建模拟 ===
  
One problem with over use of mocking is that it couples your tests to the
+
过度使用模拟的一个问题是它将您的测试与模拟的实现相结合,而不是您的真实代码。 假设您有一个实现 <code>some_method</code> 的类。 在另一个类的测试中,您提供了 '''' 提供 <code>some_method</code> 的此对象的模拟。 如果稍后您重构第一个类,使其不再具有 <code>some_method</code> - 那么即使您的代码现在已损坏,您的测试仍将继续通过!
implementation of your mocks rather than your real code. Suppose you have a
 
class that implements <code>some_method</code>. In a test for another class, you
 
provide a mock of this object that ''also'' provides <code>some_method</code>. If later
 
you refactor the first class, so that it no longer has <code>some_method</code> - then
 
your tests will continue to pass even though your code is now broken!
 
  
[[../unittest.mock#unittest.mock|<code>Mock</code>]] allows you to provide an object as a specification for the mock,
+
[[../unittest.mock#unittest.mock|Mock]] 允许您提供一个对象作为模拟的规范,使用 ''spec'' 关键字参数。 访问您的规范对象上不存在的模拟上的方法/属性将立即引发属性错误。 如果您更改规范的实现,则使用该类的测试将立即开始失败,而无需在这些测试中实例化该类。
using the ''spec'' keyword argument. Accessing methods / attributes on the
 
mock that don't exist on your specification object will immediately raise an
 
attribute error. If you change the implementation of your specification, then
 
tests that use that class will start failing immediately without you having to
 
instantiate the class in those tests.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第483行: 第424行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock(spec=SomeClass)
+
<syntaxhighlight lang="python">>>> mock = Mock(spec=SomeClass)
&gt;&gt;&gt; mock.old_method()
+
>>> mock.old_method()
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
   ...
 
   ...
AttributeError: object has no attribute 'old_method'</pre>
+
AttributeError: object has no attribute 'old_method'</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Using a specification also enables a smarter matching of calls made to the
+
使用规范还可以更智能地匹配对模拟的调用,无论某些参数是作为位置参数还是命名参数传递的:
mock, regardless of whether some parameters were passed as positional or
 
named arguments:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第500行: 第439行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; def f(a, b, c): pass
+
<syntaxhighlight lang="python3">>>> def f(a, b, c): pass
 
...
 
...
&gt;&gt;&gt; mock = Mock(spec=f)
+
>>> mock = Mock(spec=f)
&gt;&gt;&gt; mock(1, 2, 3)
+
>>> mock(1, 2, 3)
&lt;Mock name='mock()' id='140161580456576'&gt;
+
<Mock name='mock()' id='140161580456576'>
&gt;&gt;&gt; mock.assert_called_with(a=1, b=2, c=3)</pre>
+
>>> mock.assert_called_with(a=1, b=2, c=3)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If you want this smarter matching to also work with method calls on the mock,
+
如果您希望这种更智能的匹配也适用于模拟上的方法调用,您可以使用 [[../unittest|auto-speccing]]
you can use [[../unittest|<span class="std std-ref">auto-speccing</span>]].
 
  
If you want a stronger form of specification that prevents the setting
+
如果您想要一种更强大的规范形式来防止设置任意属性以及获取它们,那么您可以使用 ''spec_set'' 而不是 ''spec''
of arbitrary attributes as well as the getting of them then you can use
 
''spec_set'' instead of ''spec''.
 
  
  
第523行: 第459行:
 
<div id="patch-decorators" class="section">
 
<div id="patch-decorators" class="section">
  
== Patch Decorators ==
+
== 补丁装饰器 ==
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
With [[../unittest.mock#unittest.mock|<code>patch()</code>]] it matters that you patch objects in the namespace where
+
使用 [[../unittest.mock#unittest.mock|patch()]] 重要的是您在查找它们的命名空间中修补对象。 这通常很简单,但要获得快速指南,请阅读 [[../unittest|在哪里打补丁]]
they are looked up. This is normally straightforward, but for a quick guide
 
read [[../unittest|<span class="std std-ref">where to patch</span>]].
 
  
  
 
</div>
 
</div>
A common need in tests is to patch a class attribute or a module attribute,
+
测试中的一个常见需求是修补类属性或模块属性,例如修补内置模块或修补模块中的类以测试它是否已实例化。 模块和类实际上是全局的,因此必须在测试后取消对它们的修补,否则修补程序将持续存在于其他测试中并导致难以诊断的问题。
for example patching a builtin or patching a class in a module to test that it
 
is instantiated. Modules and classes are effectively global, so patching on
 
them has to be undone after the test or the patch will persist into other
 
tests and cause hard to diagnose problems.
 
  
mock provides three convenient decorators for this: [[../unittest.mock#unittest.mock|<code>patch()</code>]], [[../unittest.mock#unittest.mock.patch|<code>patch.object()</code>]] and
+
mock 为此提供了三个方便的装饰器:[[../unittest.mock#unittest.mock|patch()]][[../unittest.mock#unittest.mock.patch|patch.object()]] [[../unittest.mock#unittest.mock.patch|patch.dict()]]<code>patch</code> 采用单个字符串,形式为 <code>package.module.Class.attribute</code> 来指定您正在修补的属性。 它还可选地采用您希望替换属性(或类或其他)的值。 'patch.object' 接受一个对象和你想要修补的属性的名称,加上可选的值来修补它。
[[../unittest.mock#unittest.mock.patch|<code>patch.dict()</code>]]. <code>patch</code> takes a single string, of the form
 
<code>package.module.Class.attribute</code> to specify the attribute you are patching. It
 
also optionally takes a value that you want the attribute (or class or
 
whatever) to be replaced with. 'patch.object' takes an object and the name of
 
the attribute you would like patched, plus optionally the value to patch it
 
with.
 
  
<code>patch.object</code>:
+
<code>patch.object</code>
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第555行: 第479行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; original = SomeClass.attribute
+
<syntaxhighlight lang="python3">>>> original = SomeClass.attribute
&gt;&gt;&gt; @patch.object(SomeClass, 'attribute', sentinel.attribute)
+
>>> @patch.object(SomeClass, 'attribute', sentinel.attribute)
 
... def test():
 
... def test():
 
...    assert SomeClass.attribute == sentinel.attribute
 
...    assert SomeClass.attribute == sentinel.attribute
 
...
 
...
&gt;&gt;&gt; test()
+
>>> test()
&gt;&gt;&gt; assert SomeClass.attribute == original
+
>>> assert SomeClass.attribute == original
  
&gt;&gt;&gt; @patch('package.module.attribute', sentinel.attribute)
+
>>> @patch('package.module.attribute', sentinel.attribute)
 
... def test():
 
... def test():
 
...    from package.module import attribute
 
...    from package.module import attribute
 
...    assert attribute is sentinel.attribute
 
...    assert attribute is sentinel.attribute
 
...
 
...
&gt;&gt;&gt; test()</pre>
+
>>> test()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If you are patching a module (including [[../builtins#module-builtins|<code>builtins</code>]]) then use [[../unittest.mock#unittest.mock|<code>patch()</code>]]
+
如果您正在修补模块(包括 [[../builtins#module-builtins|builtins]]),则使用 [[../unittest.mock#unittest.mock|patch()]] 而不是 [[../unittest.mock#unittest.mock.patch|patch.object()]]
instead of [[../unittest.mock#unittest.mock.patch|<code>patch.object()</code>]]:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第580行: 第503行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = MagicMock(return_value=sentinel.file_handle)
+
<syntaxhighlight lang="python">>>> mock = MagicMock(return_value=sentinel.file_handle)
&gt;&gt;&gt; with patch('builtins.open', mock):
+
>>> with patch('builtins.open', mock):
 
...    handle = open('filename', 'r')
 
...    handle = open('filename', 'r')
 
...
 
...
&gt;&gt;&gt; mock.assert_called_with('filename', 'r')
+
>>> mock.assert_called_with('filename', 'r')
&gt;&gt;&gt; assert handle == sentinel.file_handle, &quot;incorrect file handle returned&quot;</pre>
+
>>> assert handle == sentinel.file_handle, "incorrect file handle returned"</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The module name can be 'dotted', in the form <code>package.module</code> if needed:
+
如果需要,模块名称可以“带点”,格式为 <code>package.module</code>
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第596行: 第519行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; @patch('package.module.ClassName.attribute', sentinel.attribute)
+
<syntaxhighlight lang="python3">>>> @patch('package.module.ClassName.attribute', sentinel.attribute)
 
... def test():
 
... def test():
 
...    from package.module import ClassName
 
...    from package.module import ClassName
 
...    assert ClassName.attribute == sentinel.attribute
 
...    assert ClassName.attribute == sentinel.attribute
 
...
 
...
&gt;&gt;&gt; test()</pre>
+
>>> test()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
A nice pattern is to actually decorate test methods themselves:
+
一个不错的模式是实际装饰测试方法本身:
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第612行: 第535行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class MyTest(unittest.TestCase):
+
<syntaxhighlight lang="python">>>> class MyTest(unittest.TestCase):
 
...    @patch.object(SomeClass, 'attribute', sentinel.attribute)
 
...    @patch.object(SomeClass, 'attribute', sentinel.attribute)
 
...    def test_something(self):
 
...    def test_something(self):
 
...        self.assertEqual(SomeClass.attribute, sentinel.attribute)
 
...        self.assertEqual(SomeClass.attribute, sentinel.attribute)
 
...
 
...
&gt;&gt;&gt; original = SomeClass.attribute
+
>>> original = SomeClass.attribute
&gt;&gt;&gt; MyTest('test_something').test_something()
+
>>> MyTest('test_something').test_something()
&gt;&gt;&gt; assert SomeClass.attribute == original</pre>
+
>>> assert SomeClass.attribute == original</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If you want to patch with a Mock, you can use [[../unittest.mock#unittest.mock|<code>patch()</code>]] with only one argument
+
如果你想用一个 Mock 打补丁,你可以使用只有一个参数的 [[../unittest.mock#unittest.mock|patch()]] (或带有两个参数的 [[../unittest.mock#unittest.mock.patch|patch.object()]] )。 将为您创建模拟并传递给测试函数/方法:
(or [[../unittest.mock#unittest.mock.patch|<code>patch.object()</code>]] with two arguments). The mock will be created for you and
 
passed into the test function / method:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第632行: 第553行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class MyTest(unittest.TestCase):
+
<syntaxhighlight lang="python">>>> class MyTest(unittest.TestCase):
 
...    @patch.object(SomeClass, 'static_method')
 
...    @patch.object(SomeClass, 'static_method')
 
...    def test_something(self, mock_method):
 
...    def test_something(self, mock_method):
第638行: 第559行:
 
...        mock_method.assert_called_with()
 
...        mock_method.assert_called_with()
 
...
 
...
&gt;&gt;&gt; MyTest('test_something').test_something()</pre>
+
>>> MyTest('test_something').test_something()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
You can stack up multiple patch decorators using this pattern:
+
您可以使用此模式堆叠多个补丁装饰器:
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第649行: 第570行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class MyTest(unittest.TestCase):
+
<syntaxhighlight lang="python3">>>> class MyTest(unittest.TestCase):
 
...    @patch('package.module.ClassName1')
 
...    @patch('package.module.ClassName1')
 
...    @patch('package.module.ClassName2')
 
...    @patch('package.module.ClassName2')
第656行: 第577行:
 
...        self.assertIs(package.module.ClassName2, MockClass2)
 
...        self.assertIs(package.module.ClassName2, MockClass2)
 
...
 
...
&gt;&gt;&gt; MyTest('test_something').test_something()</pre>
+
>>> MyTest('test_something').test_something()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
When you nest patch decorators the mocks are passed in to the decorated
+
当您嵌套补丁装饰器时,模拟以它们应用的相同顺序传递给装饰函数(应用装饰器的正常 ''Python'' 顺序)。 这意味着自下而上,因此在上面的示例中,首先传入 <code>test_module.ClassName2</code> 的模拟。
function in the same order they applied (the normal ''Python'' order that
 
decorators are applied). This means from the bottom up, so in the example
 
above the mock for <code>test_module.ClassName2</code> is passed in first.
 
  
There is also [[../unittest.mock#unittest.mock.patch|<code>patch.dict()</code>]] for setting values in a dictionary just
+
还有 [[../unittest.mock#unittest.mock.patch|patch.dict()]] 用于在范围内设置字典中的值,并在测试结束时将字典恢复到其原始状态:
during a scope and restoring the dictionary to its original state when the test
 
ends:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第674行: 第590行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; foo = {'key': 'value'}
+
<syntaxhighlight lang="python">>>> foo = {'key': 'value'}
&gt;&gt;&gt; original = foo.copy()
+
>>> original = foo.copy()
&gt;&gt;&gt; with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
+
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
 
...    assert foo == {'newkey': 'newvalue'}
 
...    assert foo == {'newkey': 'newvalue'}
 
...
 
...
&gt;&gt;&gt; assert foo == original</pre>
+
>>> assert foo == original</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<code>patch</code>, <code>patch.object</code> and <code>patch.dict</code> can all be used as context managers.
+
<code>patch</code><code>patch.object</code> <code>patch.dict</code> 都可以用作上下文管理器。
  
Where you use [[../unittest.mock#unittest.mock|<code>patch()</code>]] to create a mock for you, you can get a reference to the
+
在您使用 [[../unittest.mock#unittest.mock|patch()]] 为您创建模拟的地方,您可以使用 with 语句的“as”形式获取对模拟的引用:
mock using the &quot;as&quot; form of the with statement:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第693行: 第608行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class ProductionClass:
+
<syntaxhighlight lang="python">>>> class ProductionClass:
 
...    def method(self):
 
...    def method(self):
 
...        pass
 
...        pass
 
...
 
...
&gt;&gt;&gt; with patch.object(ProductionClass, 'method') as mock_method:
+
>>> with patch.object(ProductionClass, 'method') as mock_method:
 
...    mock_method.return_value = None
 
...    mock_method.return_value = None
 
...    real = ProductionClass()
 
...    real = ProductionClass()
 
...    real.method(1, 2, 3)
 
...    real.method(1, 2, 3)
 
...
 
...
&gt;&gt;&gt; mock_method.assert_called_with(1, 2, 3)</pre>
+
>>> mock_method.assert_called_with(1, 2, 3)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
As an alternative <code>patch</code>, <code>patch.object</code> and <code>patch.dict</code> can be used as
+
作为替代 <code>patch</code><code>patch.object</code> <code>patch.dict</code> 可以用作类装饰器。 以这种方式使用时,它与将装饰器单独应用于名称以“test”开头的每个方法相同。
class decorators. When used in this way it is the same as applying the
 
decorator individually to every method whose name starts with &quot;test&quot;.
 
  
  
第716行: 第629行:
  
 
<span id="id1"></span>
 
<span id="id1"></span>
== Further Examples ==
+
== 进一步的例子 ==
  
Here are some more examples for some slightly more advanced scenarios.
+
下面是一些稍微更高级的场景的更多示例。
  
 
<div id="mocking-chained-calls" class="section">
 
<div id="mocking-chained-calls" class="section">
  
=== Mocking chained calls ===
+
=== 模拟链式调用 ===
  
Mocking chained calls is actually straightforward with mock once you
+
一旦你理解了 [[../unittest.mock#unittest.mock.Mock|return_value]] 属性,模拟链调用实际上很简单。 当第一次调用模拟时,或者在调用之前获取它的 <code>return_value</code> 时,会创建一个新的 [[../unittest.mock#unittest.mock|Mock]]
understand the [[../unittest.mock#unittest.mock.Mock|<code>return_value</code>]] attribute. When a mock is called for
 
the first time, or you fetch its <code>return_value</code> before it has been called, a
 
new [[../unittest.mock#unittest.mock|<code>Mock</code>]] is created.
 
  
This means that you can see how the object returned from a call to a mocked
+
这意味着您可以通过询问 <code>return_value</code> 模拟来了解从调用模拟对象返回的对象是如何被使用的:
object has been used by interrogating the <code>return_value</code> mock:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第736行: 第645行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock()
+
<syntaxhighlight lang="python">>>> mock = Mock()
&gt;&gt;&gt; mock().foo(a=2, b=3)
+
>>> mock().foo(a=2, b=3)
&lt;Mock name='mock().foo()' id='...'&gt;
+
<Mock name='mock().foo()' id='...'>
&gt;&gt;&gt; mock.return_value.foo.assert_called_with(a=2, b=3)</pre>
+
>>> mock.return_value.foo.assert_called_with(a=2, b=3)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
From here it is a simple step to configure and then make assertions about
+
从这里开始,这是一个简单的配置步骤,然后对链式调用进行断言。 当然,另一种选择是首先以更可测试的方式编写代码……
chained calls. Of course another alternative is writing your code in a more
 
testable way in the first place...
 
  
So, suppose we have some code that looks a little bit like this:
+
所以,假设我们有一些看起来像这样的代码:
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第754行: 第661行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class Something:
+
<syntaxhighlight lang="python">>>> class Something:
 
...    def __init__(self):
 
...    def __init__(self):
 
...        self.backend = BackendProvider()
 
...        self.backend = BackendProvider()
 
...    def method(self):
 
...    def method(self):
 
...        response = self.backend.get_endpoint('foobar').create_call('spam', 'eggs').start_call()
 
...        response = self.backend.get_endpoint('foobar').create_call('spam', 'eggs').start_call()
...        # more code</pre>
+
...        # more code</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Assuming that <code>BackendProvider</code> is already well tested, how do we test
+
假设<code>BackendProvider</code>已经测试好了,我们如何测试<code>method()</code>? 具体来说,我们要测试代码段 <code># more code</code> 是否以正确的方式使用响应对象。
<code>method()</code>? Specifically, we want to test that the code section <code># more code</code> uses the response object in the correct way.
 
  
As this chain of calls is made from an instance attribute we can monkey patch
+
由于此调用链是从实例属性进行的,我们可以在 <code>Something</code> 实例上对 <code>backend</code> 属性进行猴子修补。 在这种特殊情况下,我们只对最终调用 <code>start_call</code> 的返回值感兴趣,因此我们没有太多配置要做。 让我们假设它返回的对象是“类文件”,因此我们将确保我们的响应对象使用内置的 [[../functions#open|open()]] 作为其 <code>spec</code>
the <code>backend</code> attribute on a <code>Something</code> instance. In this particular case
 
we are only interested in the return value from the final call to
 
<code>start_call</code> so we don't have much configuration to do. Let's assume the
 
object it returns is 'file-like', so we'll ensure that our response object
 
uses the builtin [[../functions#open|<code>open()</code>]] as its <code>spec</code>.
 
  
To do this we create a mock instance as our mock backend and create a mock
+
为此,我们创建一个模拟实例作为我们的模拟后端,并为其创建一个模拟响应对象。 要将响应设置为最终 <code>start_call</code> 的返回值,我们可以这样做:
response object for it. To set the response as the return value for that final
 
<code>start_call</code> we could do this:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第782行: 第681行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>mock_backend.get_endpoint.return_value.create_call.return_value.start_call.return_value = mock_response</pre>
+
<syntaxhighlight lang="python3">mock_backend.get_endpoint.return_value.create_call.return_value.start_call.return_value = mock_response</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
We can do that in a slightly nicer way using the [[../unittest.mock#unittest.mock.Mock|<code>configure_mock()</code>]]
+
我们可以使用 [[../unittest.mock#unittest.mock.Mock|configure_mock()]] 方法以更好的方式直接为我们设置返回值:
method to directly set the return value for us:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第794行: 第692行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; something = Something()
+
<syntaxhighlight lang="python3">>>> something = Something()
&gt;&gt;&gt; mock_response = Mock(spec=open)
+
>>> mock_response = Mock(spec=open)
&gt;&gt;&gt; mock_backend = Mock()
+
>>> mock_backend = Mock()
&gt;&gt;&gt; config = {'get_endpoint.return_value.create_call.return_value.start_call.return_value': mock_response}
+
>>> config = {'get_endpoint.return_value.create_call.return_value.start_call.return_value': mock_response}
&gt;&gt;&gt; mock_backend.configure_mock(**config)</pre>
+
>>> mock_backend.configure_mock(**config)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
With these we monkey patch the &quot;mock backend&quot; in place and can make the real
+
有了这些,我们就可以修补“模拟后端”,并可以进行真正的调用:
call:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第810行: 第707行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; something.backend = mock_backend
+
<syntaxhighlight lang="python3">>>> something.backend = mock_backend
&gt;&gt;&gt; something.method()</pre>
+
>>> something.method()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Using [[../unittest.mock#unittest.mock.Mock|<code>mock_calls</code>]] we can check the chained call with a single
+
使用 [[../unittest.mock#unittest.mock.Mock|mock_calls]] 我们可以使用单个断言检查链接的调用。 一个链式调用是一行代码中的多个调用,所以在<code>mock_calls</code>中会有多个条目。 我们可以使用 [[../unittest.mock#unittest.mock.call|call.call_list()]] 为我们创建这个调用列表:
assert. A chained call is several calls in one line of code, so there will be
 
several entries in <code>mock_calls</code>. We can use [[../unittest.mock#unittest.mock.call|<code>call.call_list()</code>]] to create
 
this list of calls for us:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第825行: 第719行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; chained = call.get_endpoint('foobar').create_call('spam', 'eggs').start_call()
+
<syntaxhighlight lang="python3">>>> chained = call.get_endpoint('foobar').create_call('spam', 'eggs').start_call()
&gt;&gt;&gt; call_list = chained.call_list()
+
>>> call_list = chained.call_list()
&gt;&gt;&gt; assert mock_backend.mock_calls == call_list</pre>
+
>>> assert mock_backend.mock_calls == call_list</syntaxhighlight>
  
 
</div>
 
</div>
第836行: 第730行:
 
<div id="partial-mocking" class="section">
 
<div id="partial-mocking" class="section">
  
=== Partial mocking ===
+
=== 部分嘲讽 ===
  
In some tests I wanted to mock out a call to [[../datetime#datetime.date|<code>datetime.date.today()</code>]]
+
在某些测试中,我想模拟对 [[../datetime#datetime.date|datetime.date.today()]] 的调用以返回已知日期,但我不想阻止被测代码创建新的日期对象。 不幸的是 [[../datetime#datetime|datetime.date]] 是用 C 编写的,所以我不能只是修补静态 <code>date.today()</code> 方法。
to return a known date, but I didn't want to prevent the code under test from
 
creating new date objects. Unfortunately [[../datetime#datetime|<code>datetime.date</code>]] is written in C, and
 
so I couldn't just monkey-patch out the static <code>date.today()</code> method.
 
  
I found a simple way of doing this that involved effectively wrapping the date
+
我找到了一种简单的方法来做到这一点,该方法涉及用模拟有效地包装日期类,但将构造函数的调用传递给真正的类(并返回真实的实例)。
class with a mock, but passing through calls to the constructor to the real
 
class (and returning real instances).
 
  
The [[../unittest.mock#unittest.mock|<code>patch decorator</code>]] is used here to
+
[[../unittest.mock#unittest.mock|patch 装饰器]] 用于模拟被测模块中的 <code>date</code> 类。 然后将模拟日期类的 <code>side_effect</code> 属性设置为返回真实日期的 lambda 函数。 当模拟日期类被调用时,<code>side_effect</code> 将构造并返回一个真实日期。
mock out the <code>date</code> class in the module under test. The <code>side_effect</code>
 
attribute on the mock date class is then set to a lambda function that returns
 
a real date. When the mock date class is called a real date will be
 
constructed and returned by <code>side_effect</code>.
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第857行: 第742行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; from datetime import date
+
<syntaxhighlight lang="python3">>>> from datetime import date
&gt;&gt;&gt; with patch('mymodule.date') as mock_date:
+
>>> with patch('mymodule.date') as mock_date:
 
...    mock_date.today.return_value = date(2010, 10, 8)
 
...    mock_date.today.return_value = date(2010, 10, 8)
 
...    mock_date.side_effect = lambda *args, **kw: date(*args, **kw)
 
...    mock_date.side_effect = lambda *args, **kw: date(*args, **kw)
 
...
 
...
 
...    assert mymodule.date.today() == date(2010, 10, 8)
 
...    assert mymodule.date.today() == date(2010, 10, 8)
...    assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)</pre>
+
...    assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Note that we don't patch [[../datetime#datetime|<code>datetime.date</code>]] globally, we patch <code>date</code> in the
+
请注意,我们不会全局修补 [[../datetime#datetime|datetime.date]],而是在 ''使用'' 的模块中修补 <code>date</code>。 见[[../unittest|在哪里打补丁]]
module that ''uses'' it. See [[../unittest|<span class="std std-ref">where to patch</span>]].
 
  
When <code>date.today()</code> is called a known date is returned, but calls to the
+
<code>date.today()</code> 被调用时,会返回一个已知日期,但对 <code>date(...)</code> 构造函数的调用仍然返回正常日期。 如果没有这个,你会发现自己必须使用与被测代码完全相同的算法来计算预期结果,这是一种经典的测试反模式。
<code>date(...)</code> constructor still return normal dates. Without this you can find
 
yourself having to calculate an expected result using exactly the same
 
algorithm as the code under test, which is a classic testing anti-pattern.
 
  
Calls to the date constructor are recorded in the <code>mock_date</code> attributes
+
对日期构造函数的调用记录在 <code>mock_date</code> 属性(<code>call_count</code> 和朋友)中,这也可能对您的测试有用。
(<code>call_count</code> and friends) which may also be useful for your tests.
 
  
An alternative way of dealing with mocking dates, or other builtin classes,
+
[https://williambert.online/2011/07/how-to-unit-testing-in-django-with-mocking-and-patching/ 这个博客条目] 中讨论了另一种处理模拟日期或其他内置类的方法。
is discussed in [https://williambert.online/2011/07/how-to-unit-testing-in-django-with-mocking-and-patching/ this blog entry].
 
  
  
第886行: 第765行:
 
<div id="mocking-a-generator-method" class="section">
 
<div id="mocking-a-generator-method" class="section">
  
=== Mocking a Generator Method ===
+
=== 模拟生成器方法 ===
  
A Python generator is a function or method that uses the [[../../reference/simple_stmts#yield|<code>yield</code>]] statement
+
Python 生成器是一个函数或方法,它使用 [[../../reference/simple_stmts#yield|yield]] 语句在迭代 [[#id3|1]] 时返回一系列值。
to return a series of values when iterated over [[#id3|1]].
 
  
A generator method / function is called to return the generator object. It is
+
调用生成器方法/函数以返回生成器对象。 然后迭代的是生成器对象。 迭代的协议方法是 [[../stdtypes#container|__iter__()]],所以我们可以使用 [[../unittest.mock#unittest.mock|MagicMock]] 来模拟它。
the generator object that is then iterated over. The protocol method for
 
iteration is [[../stdtypes#container|<code>__iter__()</code>]], so we can
 
mock this using a [[../unittest.mock#unittest.mock|<code>MagicMock</code>]].
 
  
Here's an example class with an &quot;iter&quot; method implemented as a generator:
+
这是一个示例类,其“iter”方法实现为生成器:
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第902行: 第777行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class Foo:
+
<syntaxhighlight lang="python">>>> class Foo:
 
...    def iter(self):
 
...    def iter(self):
 
...        for i in [1, 2, 3]:
 
...        for i in [1, 2, 3]:
 
...            yield i
 
...            yield i
 
...
 
...
&gt;&gt;&gt; foo = Foo()
+
>>> foo = Foo()
&gt;&gt;&gt; list(foo.iter())
+
>>> list(foo.iter())
[1, 2, 3]</pre>
+
[1, 2, 3]</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
How would we mock this class, and in particular its &quot;iter&quot; method?
+
我们将如何模拟这个类,尤其是它的“iter”方法?
  
To configure the values returned from the iteration (implicit in the call to
+
要配置迭代返回的值(隐含在调用 [[../stdtypes#list|list]] 中),我们需要配置调用返回的对象 <code>foo.iter()</code>
[[../stdtypes#list|<code>list</code>]]), we need to configure the object returned by the call to <code>foo.iter()</code>.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第923行: 第797行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock_foo = MagicMock()
+
<syntaxhighlight lang="python">>>> mock_foo = MagicMock()
&gt;&gt;&gt; mock_foo.iter.return_value = iter([1, 2, 3])
+
>>> mock_foo.iter.return_value = iter([1, 2, 3])
&gt;&gt;&gt; list(mock_foo.iter())
+
>>> list(mock_foo.iter())
[1, 2, 3]</pre>
+
[1, 2, 3]</syntaxhighlight>
  
 
</div>
 
</div>
第932行: 第806行:
 
</div>
 
</div>
 
; <span class="brackets">[[#id2|1]]</span>
 
; <span class="brackets">[[#id2|1]]</span>
: There are also generator expressions and more [http://www.dabeaz.com/coroutines/index.html advanced uses] of generators, but we aren't concerned about them here. A very good introduction to generators and how powerful they are is: [http://www.dabeaz.com/generators/ Generator Tricks for Systems Programmers].
+
: 还有生成器表达式和更多 [http://www.dabeaz.com/coroutines/index.html 生成器的高级用法] ,但我们在这里不关心它们。 对生成器及其强大功能的非常好的介绍:[http://www.dabeaz.com/generators/ 面向系统程序员的生成器技巧]
  
  
第938行: 第812行:
 
<div id="applying-the-same-patch-to-every-test-method" class="section">
 
<div id="applying-the-same-patch-to-every-test-method" class="section">
  
=== Applying the same patch to every test method ===
+
=== 对每个测试方法应用相同的补丁 ===
  
If you want several patches in place for multiple test methods the obvious way
+
如果您想为多个测试方法设置多个补丁,显而易见的方法是将补丁装饰器应用于每个方法。 这感觉像是不必要的重复。 对于 Python 2.6 或更新版本,您可以使用 [[../unittest.mock#unittest.mock|patch()]](以各种形式)作为类装饰器。 这将补丁应用于类上的所有测试方法。 测试方法由名称以 <code>test</code> 开头的方法标识:
is to apply the patch decorators to every method. This can feel like unnecessary
 
repetition. For Python 2.6 or more recent you can use [[../unittest.mock#unittest.mock|<code>patch()</code>]] (in all its
 
various forms) as a class decorator. This applies the patches to all test
 
methods on the class. A test method is identified by methods whose names start
 
with <code>test</code>:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第951行: 第820行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; @patch('mymodule.SomeClass')
+
<syntaxhighlight lang="python3">>>> @patch('mymodule.SomeClass')
 
... class MyTest(unittest.TestCase):
 
... class MyTest(unittest.TestCase):
 
...
 
...
第963行: 第832行:
 
...        return 'something'
 
...        return 'something'
 
...
 
...
&gt;&gt;&gt; MyTest('test_one').test_one()
+
>>> MyTest('test_one').test_one()
&gt;&gt;&gt; MyTest('test_two').test_two()
+
>>> MyTest('test_two').test_two()
&gt;&gt;&gt; MyTest('test_two').not_a_test()
+
>>> MyTest('test_two').not_a_test()
'something'</pre>
+
'something'</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
An alternative way of managing patches is to use the [[unittest.mock.html#start-and-stop|<span class="std std-ref">patch methods: start and stop</span>]].
+
管理补丁的另一种方法是使用 [[../unittest|补丁方法:start 和 stop]]。 这些允许您将补丁移动到 <code>setUp</code> <code>tearDown</code> 方法中。
These allow you to move the patching into your <code>setUp</code> and <code>tearDown</code> methods.
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第978行: 第846行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class MyTest(unittest.TestCase):
+
<syntaxhighlight lang="python3">>>> class MyTest(unittest.TestCase):
 
...    def setUp(self):
 
...    def setUp(self):
 
...        self.patcher = patch('mymodule.foo')
 
...        self.patcher = patch('mymodule.foo')
第989行: 第857行:
 
...        self.patcher.stop()
 
...        self.patcher.stop()
 
...
 
...
&gt;&gt;&gt; MyTest('test_foo').run()</pre>
+
>>> MyTest('test_foo').run()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If you use this technique you must ensure that the patching is &quot;undone&quot; by
+
如果您使用这种技术,您必须通过调用 <code>stop</code> 来确保补丁被“撤销”。 这可能比您想象的要复杂,因为如果在 setUp 中引发异常,则不会调用 tearDown。 [[../unittest#unittest.TestCase|unittest.TestCase.addCleanup()]] 使这更容易:
calling <code>stop</code>. This can be fiddlier than you might think, because if an
 
exception is raised in the setUp then tearDown is not called.
 
[[../unittest#unittest.TestCase|<code>unittest.TestCase.addCleanup()</code>]] makes this easier:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第1,003行: 第868行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class MyTest(unittest.TestCase):
+
<syntaxhighlight lang="python3">>>> class MyTest(unittest.TestCase):
 
...    def setUp(self):
 
...    def setUp(self):
 
...        patcher = patch('mymodule.foo')
 
...        patcher = patch('mymodule.foo')
第1,012行: 第877行:
 
...        self.assertIs(mymodule.foo, self.mock_foo)
 
...        self.assertIs(mymodule.foo, self.mock_foo)
 
...
 
...
&gt;&gt;&gt; MyTest('test_foo').run()</pre>
+
>>> MyTest('test_foo').run()</syntaxhighlight>
  
 
</div>
 
</div>
第1,021行: 第886行:
 
<div id="mocking-unbound-methods" class="section">
 
<div id="mocking-unbound-methods" class="section">
  
=== Mocking Unbound Methods ===
+
=== 模拟未绑定的方法 ===
  
Whilst writing tests today I needed to patch an ''unbound method'' (patching the
+
在今天编写测试时,我需要修补一个 ''未绑定方法'' (修补类上的方法而不是实例上的方法)。 我需要将 self 作为第一个参数传入,因为我想断言哪些对象正在调用此特定方法。 问题是您无法为此使用模拟进行修补,因为如果您用模拟替换未绑定的方法,则从实例中获取时它不会成为绑定方法,因此不会自我传入。 解决方法是用一个真正的函数来修补未绑定的方法。 [[../unittest.mock#unittest.mock|patch()]] 装饰器使得用模拟修补方法变得如此简单,以至于必须创建一个真正的函数变得很麻烦。
method on the class rather than on the instance). I needed self to be passed
 
in as the first argument because I want to make asserts about which objects
 
were calling this particular method. The issue is that you can't patch with a
 
mock for this, because if you replace an unbound method with a mock it doesn't
 
become a bound method when fetched from the instance, and so it doesn't get
 
self passed in. The workaround is to patch the unbound method with a real
 
function instead. The [[../unittest.mock#unittest.mock|<code>patch()</code>]] decorator makes it so simple to
 
patch out methods with a mock that having to create a real function becomes a
 
nuisance.
 
  
If you pass <code>autospec=True</code> to patch then it does the patching with a
+
如果您将 <code>autospec=True</code> 传递给 patch,那么它会使用 ''real'' 函数对象进行修补。 这个函数对象与它正在替换的函数对象具有相同的签名,但在幕后委托给一个模拟。 您仍然以与以前完全相同的方式自动创建模拟。 但这意味着,如果您使用它来修补类上的未绑定方法,则如果从实例中获取模拟函数,则该模拟函数将转换为绑定方法。 它将 <code>self</code> 作为第一个参数传入,这正是我想要的:
''real'' function object. This function object has the same signature as the one
 
it is replacing, but delegates to a mock under the hood. You still get your
 
mock auto-created in exactly the same way as before. What it means though, is
 
that if you use it to patch out an unbound method on a class the mocked
 
function will be turned into a bound method if it is fetched from an instance.
 
It will have <code>self</code> passed in as the first argument, which is exactly what I
 
wanted:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,047行: 第896行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class Foo:
+
<syntaxhighlight lang="python">>>> class Foo:
 
...  def foo(self):
 
...  def foo(self):
 
...    pass
 
...    pass
 
...
 
...
&gt;&gt;&gt; with patch.object(Foo, 'foo', autospec=True) as mock_foo:
+
>>> with patch.object(Foo, 'foo', autospec=True) as mock_foo:
 
...  mock_foo.return_value = 'foo'
 
...  mock_foo.return_value = 'foo'
 
...  foo = Foo()
 
...  foo = Foo()
第1,057行: 第906行:
 
...
 
...
 
'foo'
 
'foo'
&gt;&gt;&gt; mock_foo.assert_called_once_with(foo)</pre>
+
>>> mock_foo.assert_called_once_with(foo)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If we don't use <code>autospec=True</code> then the unbound method is patched out
+
如果我们不使用 <code>autospec=True</code>,那么未绑定的方法会用 Mock 实例修补,而不是用 <code>self</code> 调用。
with a Mock instance instead, and isn't called with <code>self</code>.
 
  
  
第1,069行: 第917行:
 
<div id="checking-multiple-calls-with-mock" class="section">
 
<div id="checking-multiple-calls-with-mock" class="section">
  
=== Checking multiple calls with mock ===
+
=== 使用模拟检查多个调用 ===
  
mock has a nice API for making assertions about how your mock objects are used.
+
mock 有一个很好的 API 来断言你的模拟对象是如何使用的。
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,077行: 第925行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock()
+
<syntaxhighlight lang="python">>>> mock = Mock()
&gt;&gt;&gt; mock.foo_bar.return_value = None
+
>>> mock.foo_bar.return_value = None
&gt;&gt;&gt; mock.foo_bar('baz', spam='eggs')
+
>>> mock.foo_bar('baz', spam='eggs')
&gt;&gt;&gt; mock.foo_bar.assert_called_with('baz', spam='eggs')</pre>
+
>>> mock.foo_bar.assert_called_with('baz', spam='eggs')</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If your mock is only being called once you can use the
+
如果您的模拟只被调用一次,您可以使用 <code>assert_called_once_with()</code> 方法,该方法也断言 <code>call_count</code> 是一个。
<code>assert_called_once_with()</code> method that also asserts that the
 
<code>call_count</code> is one.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,093行: 第939行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock.foo_bar.assert_called_once_with('baz', spam='eggs')
+
<syntaxhighlight lang="python">>>> mock.foo_bar.assert_called_once_with('baz', spam='eggs')
&gt;&gt;&gt; mock.foo_bar()
+
>>> mock.foo_bar()
&gt;&gt;&gt; mock.foo_bar.assert_called_once_with('baz', spam='eggs')
+
>>> mock.foo_bar.assert_called_once_with('baz', spam='eggs')
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
     ...
 
     ...
AssertionError: Expected to be called once. Called 2 times.</pre>
+
AssertionError: Expected to be called once. Called 2 times.</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Both <code>assert_called_with</code> and <code>assert_called_once_with</code> make assertions about
+
<code>assert_called_with</code> <code>assert_called_once_with</code> 都对 ''最近的'' 调用做出断言。 如果您的模拟将被多次调用,并且您想对 ''all'' 这些调用进行断言,您可以使用 [[../unittest.mock#unittest.mock.Mock|call_args_list]]
the ''most recent'' call. If your mock is going to be called several times, and
 
you want to make assertions about ''all'' those calls you can use
 
[[../unittest.mock#unittest.mock.Mock|<code>call_args_list</code>]]:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,112行: 第955行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock(return_value=None)
+
<syntaxhighlight lang="python">>>> mock = Mock(return_value=None)
&gt;&gt;&gt; mock(1, 2, 3)
+
>>> mock(1, 2, 3)
&gt;&gt;&gt; mock(4, 5, 6)
+
>>> mock(4, 5, 6)
&gt;&gt;&gt; mock()
+
>>> mock()
&gt;&gt;&gt; mock.call_args_list
+
>>> mock.call_args_list
[call(1, 2, 3), call(4, 5, 6), call()]</pre>
+
[call(1, 2, 3), call(4, 5, 6), call()]</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The [[../unittest.mock#unittest.mock|<code>call</code>]] helper makes it easy to make assertions about these calls. You
+
[[../unittest.mock#unittest.mock|call]] 助手可以轻松地对这些调用进行断言。 您可以建立一个预期调用列表并将其与 <code>call_args_list</code> 进行比较。 这看起来与 <code>call_args_list</code> 的再现非常相似:
can build up a list of expected calls and compare it to <code>call_args_list</code>. This
 
looks remarkably similar to the repr of the <code>call_args_list</code>:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,130行: 第971行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; expected = [call(1, 2, 3), call(4, 5, 6), call()]
+
<syntaxhighlight lang="python">>>> expected = [call(1, 2, 3), call(4, 5, 6), call()]
&gt;&gt;&gt; mock.call_args_list == expected
+
>>> mock.call_args_list == expected
True</pre>
+
True</syntaxhighlight>
  
 
</div>
 
</div>
第1,141行: 第982行:
 
<div id="coping-with-mutable-arguments" class="section">
 
<div id="coping-with-mutable-arguments" class="section">
  
=== Coping with mutable arguments ===
+
=== 处理可变参数 ===
  
Another situation is rare, but can bite you, is when your mock is called with
+
另一种情况很少见,但会咬你,就是当你的模拟被可变参数调用时。 <code>call_args</code> <code>call_args_list</code> 存储对参数的 ''引用'' 。 如果参数被测试中的代码改变了,那么你不能再断言调用模拟时的值是什么。
mutable arguments. <code>call_args</code> and <code>call_args_list</code> store ''references'' to the
 
arguments. If the arguments are mutated by the code under test then you can no
 
longer make assertions about what the values were when the mock was called.
 
  
Here's some example code that shows the problem. Imagine the following functions
+
这是一些显示问题的示例代码。 想象一下在“mymodule”中定义的以下函数:
defined in 'mymodule':
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第1,155行: 第992行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>def frob(val):
+
<syntaxhighlight lang="python3">def frob(val):
 
     pass
 
     pass
  
 
def grob(val):
 
def grob(val):
     &quot;First frob and then clear val&quot;
+
     "First frob and then clear val"
 
     frob(val)
 
     frob(val)
     val.clear()</pre>
+
     val.clear()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
When we try to test that <code>grob</code> calls <code>frob</code> with the correct argument look
+
当我们尝试测试 <code>grob</code> 使用正确的参数调用 <code>frob</code> 时,看看会发生什么:
what happens:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第1,173行: 第1,009行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; with patch('mymodule.frob') as mock_frob:
+
<syntaxhighlight lang="python3">>>> with patch('mymodule.frob') as mock_frob:
 
...    val = {6}
 
...    val = {6}
 
...    mymodule.grob(val)
 
...    mymodule.grob(val)
 
...
 
...
&gt;&gt;&gt; val
+
>>> val
 
set()
 
set()
&gt;&gt;&gt; mock_frob.assert_called_with({6})
+
>>> mock_frob.assert_called_with({6})
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
     ...
 
     ...
 
AssertionError: Expected: (({6},), {})
 
AssertionError: Expected: (({6},), {})
Called with: ((set(),), {})</pre>
+
Called with: ((set(),), {})</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
One possibility would be for mock to copy the arguments you pass in. This
+
一种可能性是模拟复制您传入的参数。 如果你做的断言依赖于对象身份的相等性,那么这可能会导致问题。
could then cause problems if you do assertions that rely on object identity
 
for equality.
 
  
Here's one solution that uses the <code>side_effect</code>
+
这是使用 <code>side_effect</code> 功能的一种解决方案。 如果您为模拟提供 <code>side_effect</code> 函数,则将使用与模拟相同的参数调用 <code>side_effect</code>。 这使我们有机会复制参数并将它们存储以供以后的断言使用。 在这个例子中,我使用 ''another'' 模拟来存储参数,以便我可以使用模拟方法进行断言。 一个辅助函数再次为我设置了这个。
functionality. If you provide a <code>side_effect</code> function for a mock then
 
<code>side_effect</code> will be called with the same args as the mock. This gives us an
 
opportunity to copy the arguments and store them for later assertions. In this
 
example I'm using ''another'' mock to store the arguments so that I can use the
 
mock methods for doing the assertion. Again a helper function sets this up for
 
me.
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第1,204行: 第1,032行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; from copy import deepcopy
+
<syntaxhighlight lang="python3">>>> from copy import deepcopy
&gt;&gt;&gt; from unittest.mock import Mock, patch, DEFAULT
+
>>> from unittest.mock import Mock, patch, DEFAULT
&gt;&gt;&gt; def copy_call_args(mock):
+
>>> def copy_call_args(mock):
 
...    new_mock = Mock()
 
...    new_mock = Mock()
 
...    def side_effect(*args, **kwargs):
 
...    def side_effect(*args, **kwargs):
第1,216行: 第1,044行:
 
...    return new_mock
 
...    return new_mock
 
...
 
...
&gt;&gt;&gt; with patch('mymodule.frob') as mock_frob:
+
>>> with patch('mymodule.frob') as mock_frob:
 
...    new_mock = copy_call_args(mock_frob)
 
...    new_mock = copy_call_args(mock_frob)
 
...    val = {6}
 
...    val = {6}
 
...    mymodule.grob(val)
 
...    mymodule.grob(val)
 
...
 
...
&gt;&gt;&gt; new_mock.assert_called_with({6})
+
>>> new_mock.assert_called_with({6})
&gt;&gt;&gt; new_mock.call_args
+
>>> new_mock.call_args
call({6})</pre>
+
call({6})</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
<code>copy_call_args</code> is called with the mock that will be called. It returns a new
+
<code>copy_call_args</code> 与将被调用的模拟一起调用。 它返回一个我们对其进行断言的新模拟。 <code>side_effect</code> 函数制作了 args 的副本,并使用副本调用我们的 <code>new_mock</code>
mock that we do the assertion on. The <code>side_effect</code> function makes a copy of
 
the args and calls our <code>new_mock</code> with the copy.
 
  
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
If your mock is only going to be used once there is an easier way of
+
如果您的模拟只使用一次,则有一种更简单的方法可以在调用时检查参数。 您可以简单地在 <code>side_effect</code> 函数内部进行检查。
checking arguments at the point they are called. You can simply do the
 
checking inside a <code>side_effect</code> function.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,244行: 第1,068行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; def side_effect(arg):
+
<syntaxhighlight lang="python">>>> def side_effect(arg):
 
...    assert arg == {6}
 
...    assert arg == {6}
 
...
 
...
&gt;&gt;&gt; mock = Mock(side_effect=side_effect)
+
>>> mock = Mock(side_effect=side_effect)
&gt;&gt;&gt; mock({6})
+
>>> mock({6})
&gt;&gt;&gt; mock(set())
+
>>> mock(set())
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
     ...
 
     ...
AssertionError</pre>
+
AssertionError</syntaxhighlight>
  
 
</div>
 
</div>
第1,259行: 第1,083行:
  
 
</div>
 
</div>
An alternative approach is to create a subclass of [[../unittest.mock#unittest.mock|<code>Mock</code>]] or
+
另一种方法是创建一个 [[../unittest.mock#unittest.mock|Mock]] [[../unittest.mock#unittest.mock|MagicMock]] 的子类来复制(使用 [[../copy#copy|copy.deepcopy()]])参数。 这是一个示例实现:
[[../unittest.mock#unittest.mock|<code>MagicMock</code>]] that copies (using [[../copy#copy|<code>copy.deepcopy()</code>]]) the arguments.
 
Here's an example implementation:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,267行: 第1,089行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; from copy import deepcopy
+
<syntaxhighlight lang="python">>>> from copy import deepcopy
&gt;&gt;&gt; class CopyingMock(MagicMock):
+
>>> class CopyingMock(MagicMock):
 
...    def __call__(self, /, *args, **kwargs):
 
...    def __call__(self, /, *args, **kwargs):
 
...        args = deepcopy(args)
 
...        args = deepcopy(args)
 
...        kwargs = deepcopy(kwargs)
 
...        kwargs = deepcopy(kwargs)
...        return super(CopyingMock, self).__call__(*args, **kwargs)
+
...        return super().__call__(*args, **kwargs)
 
...
 
...
&gt;&gt;&gt; c = CopyingMock(return_value=None)
+
>>> c = CopyingMock(return_value=None)
&gt;&gt;&gt; arg = set()
+
>>> arg = set()
&gt;&gt;&gt; c(arg)
+
>>> c(arg)
&gt;&gt;&gt; arg.add(1)
+
>>> arg.add(1)
&gt;&gt;&gt; c.assert_called_with(set())
+
>>> c.assert_called_with(set())
&gt;&gt;&gt; c.assert_called_with(arg)
+
>>> c.assert_called_with(arg)
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
     ...
 
     ...
 
AssertionError: Expected call: mock({1})
 
AssertionError: Expected call: mock({1})
 
Actual call: mock(set())
 
Actual call: mock(set())
&gt;&gt;&gt; c.foo
+
>>> c.foo
&lt;CopyingMock name='mock.foo' id='...'&gt;</pre>
+
<CopyingMock name='mock.foo' id='...'></syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
When you subclass <code>Mock</code> or <code>MagicMock</code> all dynamically created attributes,
+
当您对 <code>Mock</code> <code>MagicMock</code> 所有动态创建的属性进行子类化时,<code>return_value</code> 将自动使用您的子类。 这意味着 <code>CopyingMock</code> 的所有子代也将具有 <code>CopyingMock</code> 类型。
and the <code>return_value</code> will use your subclass automatically. That means all
 
children of a <code>CopyingMock</code> will also have the type <code>CopyingMock</code>.
 
  
  
第1,298行: 第1,118行:
 
<div id="nesting-patches" class="section">
 
<div id="nesting-patches" class="section">
  
=== Nesting Patches ===
+
=== 嵌套补丁 ===
  
Using patch as a context manager is nice, but if you do multiple patches you
+
使用补丁作为上下文管理器很好,但如果你做多个补丁,你最终可能会嵌套使用越来越向右缩进的语句:
can end up with nested with statements indenting further and further to the
 
right:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第1,308行: 第1,126行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class MyTest(unittest.TestCase):
+
<syntaxhighlight lang="python3">>>> class MyTest(unittest.TestCase):
 
...
 
...
 
...    def test_foo(self):
 
...    def test_foo(self):
第1,318行: 第1,136行:
 
...                    assert mymodule.Spam is mock_spam
 
...                    assert mymodule.Spam is mock_spam
 
...
 
...
&gt;&gt;&gt; original = mymodule.Foo
+
>>> original = mymodule.Foo
&gt;&gt;&gt; MyTest('test_foo').test_foo()
+
>>> MyTest('test_foo').test_foo()
&gt;&gt;&gt; assert mymodule.Foo is original</pre>
+
>>> assert mymodule.Foo is original</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
With unittest <code>cleanup</code> functions and the [[unittest.mock.html#start-and-stop|<span class="std std-ref">patch methods: start and stop</span>]] we can
+
使用 unittest <code>cleanup</code> 函数和 [[../unittest|补丁方法:start 和 stop]] 我们可以在没有嵌套缩进的情况下实现相同的效果。 一个简单的辅助方法 <code>create_patch</code> 将补丁放置到位并为我们返回创建的模拟:
achieve the same effect without the nested indentation. A simple helper
 
method, <code>create_patch</code>, puts the patch in place and returns the created mock
 
for us:
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第1,334行: 第1,149行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class MyTest(unittest.TestCase):
+
<syntaxhighlight lang="python3">>>> class MyTest(unittest.TestCase):
 
...
 
...
 
...    def create_patch(self, name):
 
...    def create_patch(self, name):
第1,351行: 第1,166行:
 
...        assert mymodule.Spam is mock_spam
 
...        assert mymodule.Spam is mock_spam
 
...
 
...
&gt;&gt;&gt; original = mymodule.Foo
+
>>> original = mymodule.Foo
&gt;&gt;&gt; MyTest('test_foo').run()
+
>>> MyTest('test_foo').run()
&gt;&gt;&gt; assert mymodule.Foo is original</pre>
+
>>> assert mymodule.Foo is original</syntaxhighlight>
  
 
</div>
 
</div>
第1,362行: 第1,177行:
 
<div id="mocking-a-dictionary-with-magicmock" class="section">
 
<div id="mocking-a-dictionary-with-magicmock" class="section">
  
=== Mocking a dictionary with MagicMock ===
+
=== MagicMock 模拟字典 ===
  
You may want to mock a dictionary, or other container object, recording all
+
你可能想要模拟一个字典或其他容器对象,记录对它的所有访问,同时让它仍然像字典一样运行。
access to it whilst having it still behave like a dictionary.
 
  
We can do this with [[../unittest.mock#unittest.mock|<code>MagicMock</code>]], which will behave like a dictionary,
+
我们可以使用 [[../unittest.mock#unittest.mock|MagicMock]] 来做到这一点,它的行为类似于字典,并使用 [[../unittest.mock#unittest.mock.Mock|side_effect]] 将字典访问委托给我们控制的真实底层字典。
and using [[../unittest.mock#unittest.mock.Mock|<code>side_effect</code>]] to delegate dictionary access to a real
 
underlying dictionary that is under our control.
 
  
When the <code>__getitem__()</code> and <code>__setitem__()</code> methods of our <code>MagicMock</code> are called
+
当我们的 <code>MagicMock</code> <code>__getitem__()</code> <code>__setitem__()</code> 方法被调用(正常字典访问)时,<code>side_effect</code> 将使用键调用(在 [ X154X] 值太)。 我们还可以控制返回的内容。
(normal dictionary access) then <code>side_effect</code> is called with the key (and in
 
the case of <code>__setitem__</code> the value too). We can also control what is returned.
 
  
After the <code>MagicMock</code> has been used we can use attributes like
+
使用 <code>MagicMock</code> 后,我们可以使用 [[../unittest.mock#unittest.mock.Mock|call_args_list]] 等属性来断言字典的使用方式:
[[../unittest.mock#unittest.mock.Mock|<code>call_args_list</code>]] to assert about how the dictionary was used:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,382行: 第1,191行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; my_dict = {'a': 1, 'b': 2, 'c': 3}
+
<syntaxhighlight lang="python">>>> my_dict = {'a': 1, 'b': 2, 'c': 3}
&gt;&gt;&gt; def getitem(name):
+
>>> def getitem(name):
 
...      return my_dict[name]
 
...      return my_dict[name]
 
...
 
...
&gt;&gt;&gt; def setitem(name, val):
+
>>> def setitem(name, val):
 
...    my_dict[name] = val
 
...    my_dict[name] = val
 
...
 
...
&gt;&gt;&gt; mock = MagicMock()
+
>>> mock = MagicMock()
&gt;&gt;&gt; mock.__getitem__.side_effect = getitem
+
>>> mock.__getitem__.side_effect = getitem
&gt;&gt;&gt; mock.__setitem__.side_effect = setitem</pre>
+
>>> mock.__setitem__.side_effect = setitem</syntaxhighlight>
  
 
</div>
 
</div>
第1,398行: 第1,207行:
 
<div class="admonition note">
 
<div class="admonition note">
  
注解
+
笔记
  
An alternative to using <code>MagicMock</code> is to use <code>Mock</code> and ''only'' provide
+
使用 <code>MagicMock</code> 的替代方法是使用 <code>Mock</code> ''only'' 提供您特别想要的魔术方法:
the magic methods you specifically want:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,407行: 第1,215行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock()
+
<syntaxhighlight lang="python">>>> mock = Mock()
&gt;&gt;&gt; mock.__getitem__ = Mock(side_effect=getitem)
+
>>> mock.__getitem__ = Mock(side_effect=getitem)
&gt;&gt;&gt; mock.__setitem__ = Mock(side_effect=setitem)</pre>
+
>>> mock.__setitem__ = Mock(side_effect=setitem)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
A ''third'' option is to use <code>MagicMock</code> but passing in <code>dict</code> as the ''spec''
+
''third'' 选项是使用 <code>MagicMock</code> 但传入 <code>dict</code> 作为 ''spec''(或 ''spec_set'')参数,以便<code>MagicMock</code> 创建的只有字典魔术方法可用:
(or ''spec_set'') argument so that the <code>MagicMock</code> created only has
 
dictionary magic methods available:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,422行: 第1,228行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = MagicMock(spec_set=dict)
+
<syntaxhighlight lang="python">>>> mock = MagicMock(spec_set=dict)
&gt;&gt;&gt; mock.__getitem__.side_effect = getitem
+
>>> mock.__getitem__.side_effect = getitem
&gt;&gt;&gt; mock.__setitem__.side_effect = setitem</pre>
+
>>> mock.__setitem__.side_effect = setitem</syntaxhighlight>
  
 
</div>
 
</div>
第1,431行: 第1,237行:
  
 
</div>
 
</div>
With these side effect functions in place, the <code>mock</code> will behave like a normal
+
有了这些副作用功能,<code>mock</code> 将像普通字典一样运行,但会记录访问。 如果您尝试访问不存在的密钥,它甚至会引发 [[../exceptions#KeyError|KeyError]]
dictionary but recording the access. It even raises a [[../exceptions#KeyError|<code>KeyError</code>]] if you try
 
to access a key that doesn't exist.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,439行: 第1,243行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock['a']
+
<syntaxhighlight lang="python">>>> mock['a']
 
1
 
1
&gt;&gt;&gt; mock['c']
+
>>> mock['c']
 
3
 
3
&gt;&gt;&gt; mock['d']
+
>>> mock['d']
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
     ...
 
     ...
 
KeyError: 'd'
 
KeyError: 'd'
&gt;&gt;&gt; mock['b'] = 'fish'
+
>>> mock['b'] = 'fish'
&gt;&gt;&gt; mock['d'] = 'eggs'
+
>>> mock['d'] = 'eggs'
&gt;&gt;&gt; mock['b']
+
>>> mock['b']
 
'fish'
 
'fish'
&gt;&gt;&gt; mock['d']
+
>>> mock['d']
'eggs'</pre>
+
'eggs'</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
After it has been used you can make assertions about the access using the normal
+
使用后,您可以使用正常的模拟方法和属性对访问进行断言:
mock methods and attributes:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,464行: 第1,267行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock.__getitem__.call_args_list
+
<syntaxhighlight lang="python">>>> mock.__getitem__.call_args_list
 
[call('a'), call('c'), call('d'), call('b'), call('d')]
 
[call('a'), call('c'), call('d'), call('b'), call('d')]
&gt;&gt;&gt; mock.__setitem__.call_args_list
+
>>> mock.__setitem__.call_args_list
 
[call('b', 'fish'), call('d', 'eggs')]
 
[call('b', 'fish'), call('d', 'eggs')]
&gt;&gt;&gt; my_dict
+
>>> my_dict
{'a': 1, 'b': 'fish', 'c': 3, 'd': 'eggs'}</pre>
+
{'a': 1, 'b': 'fish', 'c': 3, 'd': 'eggs'}</syntaxhighlight>
  
 
</div>
 
</div>
第1,478行: 第1,281行:
 
<div id="mock-subclasses-and-their-attributes" class="section">
 
<div id="mock-subclasses-and-their-attributes" class="section">
  
=== Mock subclasses and their attributes ===
+
=== 模拟子类及其属性 ===
  
There are various reasons why you might want to subclass [[../unittest.mock#unittest.mock|<code>Mock</code>]]. One
+
您可能想要子类化 [[../unittest.mock#unittest.mock|Mock]] 的原因有很多。 原因之一可能是添加辅助方法。 这是一个愚蠢的例子:
reason might be to add helper methods. Here's a silly example:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,487行: 第1,289行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class MyMock(MagicMock):
+
<syntaxhighlight lang="python">>>> class MyMock(MagicMock):
 
...    def has_been_called(self):
 
...    def has_been_called(self):
 
...        return self.called
 
...        return self.called
 
...
 
...
&gt;&gt;&gt; mymock = MyMock(return_value=None)
+
>>> mymock = MyMock(return_value=None)
&gt;&gt;&gt; mymock
+
>>> mymock
&lt;MyMock id='...'&gt;
+
<MyMock id='...'>
&gt;&gt;&gt; mymock.has_been_called()
+
>>> mymock.has_been_called()
 
False
 
False
&gt;&gt;&gt; mymock()
+
>>> mymock()
&gt;&gt;&gt; mymock.has_been_called()
+
>>> mymock.has_been_called()
True</pre>
+
True</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The standard behaviour for <code>Mock</code> instances is that attributes and the return
+
<code>Mock</code> 实例的标准行为是属性和返回值模拟与访问它们的模拟具有相同的类型。 这确保 <code>Mock</code> 属性为 <code>Mocks</code><code>MagicMock</code> 属性为 <code>MagicMocks</code> [[#id5|2]]。 因此,如果您要子类化以添加辅助方法,那么它们也将可用于您的子类实例的属性和返回值模拟。
value mocks are of the same type as the mock they are accessed on. This ensures
 
that <code>Mock</code> attributes are <code>Mocks</code> and <code>MagicMock</code> attributes are <code>MagicMocks</code>
 
[[#id5|2]]. So if you're subclassing to add helper methods then they'll also be
 
available on the attributes and return value mock of instances of your
 
subclass.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,514行: 第1,311行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mymock.foo
+
<syntaxhighlight lang="python">>>> mymock.foo
&lt;MyMock name='mock.foo' id='...'&gt;
+
<MyMock name='mock.foo' id='...'>
&gt;&gt;&gt; mymock.foo.has_been_called()
+
>>> mymock.foo.has_been_called()
 
False
 
False
&gt;&gt;&gt; mymock.foo()
+
>>> mymock.foo()
&lt;MyMock name='mock.foo()' id='...'&gt;
+
<MyMock name='mock.foo()' id='...'>
&gt;&gt;&gt; mymock.foo.has_been_called()
+
>>> mymock.foo.has_been_called()
True</pre>
+
True</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Sometimes this is inconvenient. For example, [https://code.google.com/archive/p/mock/issues/105 one user] is subclassing mock to
+
有时这很不方便。 例如, [https://code.google.com/archive/p/mock/issues/105 一个用户] 正在子类化模拟以创建一个 [https://twistedmatrix.com/documents/11.0.0/api/twisted.python.components.html 扭曲适配器] 。 将此应用于属性实际上会导致错误。
created a [https://twistedmatrix.com/documents/11.0.0/api/twisted.python.components.html Twisted adaptor].
 
Having this applied to attributes too actually causes errors.
 
  
<code>Mock</code> (in all its flavours) uses a method called <code>_get_child_mock</code> to create
+
<code>Mock</code>(在其所有风格中)使用称为 <code>_get_child_mock</code> 的方法为属性和返回值创建这些“子模拟”。 您可以通过覆盖此方法来防止您的子类用于属性。 签名是它接受任意关键字参数(<code>**kwargs</code>),然后将其传递给模拟构造函数:
these &quot;sub-mocks&quot; for attributes and return values. You can prevent your
 
subclass being used for attributes by overriding this method. The signature is
 
that it takes arbitrary keyword arguments (<code>**kwargs</code>) which are then passed
 
onto the mock constructor:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,540行: 第1,331行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class Subclass(MagicMock):
+
<syntaxhighlight lang="python">>>> class Subclass(MagicMock):
 
...    def _get_child_mock(self, /, **kwargs):
 
...    def _get_child_mock(self, /, **kwargs):
 
...        return MagicMock(**kwargs)
 
...        return MagicMock(**kwargs)
 
...
 
...
&gt;&gt;&gt; mymock = Subclass()
+
>>> mymock = Subclass()
&gt;&gt;&gt; mymock.foo
+
>>> mymock.foo
&lt;MagicMock name='mock.foo' id='...'&gt;
+
<MagicMock name='mock.foo' id='...'>
&gt;&gt;&gt; assert isinstance(mymock, Subclass)
+
>>> assert isinstance(mymock, Subclass)
&gt;&gt;&gt; assert not isinstance(mymock.foo, Subclass)
+
>>> assert not isinstance(mymock.foo, Subclass)
&gt;&gt;&gt; assert not isinstance(mymock(), Subclass)</pre>
+
>>> assert not isinstance(mymock(), Subclass)</syntaxhighlight>
  
 
</div>
 
</div>
第1,555行: 第1,346行:
 
</div>
 
</div>
 
; <span class="brackets">[[#id4|2]]</span>
 
; <span class="brackets">[[#id4|2]]</span>
: An exception to this rule are the non-callable mocks. Attributes use the callable variant because otherwise non-callable mocks couldn't have callable methods.
+
: 此规则的一个例外是不可调用的模拟。 属性使用可调用变体,因为否则不可调用的模拟不能具有可调用的方法。
  
  
第1,561行: 第1,352行:
 
<div id="mocking-imports-with-patch-dict" class="section">
 
<div id="mocking-imports-with-patch-dict" class="section">
  
=== Mocking imports with patch.dict ===
+
=== 使用 patch.dict 模拟导入 ===
  
One situation where mocking can be hard is where you have a local import inside
+
模拟可能很难的一种情况是您在函数中具有本地导入。 这些更难模拟,因为它们没有使用我们可以修补的模块命名空间中的对象。
a function. These are harder to mock because they aren't using an object from
 
the module namespace that we can patch out.
 
  
Generally local imports are to be avoided. They are sometimes done to prevent
+
通常应避免本地进口。 有时这样做是为了防止循环依赖,为此 ''通常'' 有更好的方法来解决问题(重构代码)或通过延迟导入来防止“前期成本”。 这也可以通过比无条件本地导入更好的方式来解决(将模块存储为类或模块属性,并且仅在第一次使用时进行导入)。
circular dependencies, for which there is ''usually'' a much better way to solve
 
the problem (refactor the code) or to prevent &quot;up front costs&quot; by delaying the
 
import. This can also be solved in better ways than an unconditional local
 
import (store the module as a class or module attribute and only do the import
 
on first use).
 
  
That aside there is a way to use <code>mock</code> to affect the results of an import.
+
除此之外,还有一种方法可以使用 <code>mock</code> 来影响导入的结果。 导入从 [[../sys#sys|sys.modules]] 字典中获取一个 ''object''。 请注意,它获取了一个 ''object'',它不需要是一个模块。 第一次导入模块会导致模块对象被放入 sys.modules,所以通常当你导入某些东西时,你会得到一个模块。 然而,情况不必如此。
Importing fetches an ''object'' from the [[../sys#sys|<code>sys.modules</code>]] dictionary. Note that it
 
fetches an ''object'', which need not be a module. Importing a module for the
 
first time results in a module object being put in sys.modules, so usually
 
when you import something you get a module back. This need not be the case
 
however.
 
  
This means you can use [[../unittest.mock#unittest.mock.patch|<code>patch.dict()</code>]] to ''temporarily'' put a mock in place
+
这意味着您可以使用 [[../unittest.mock#unittest.mock.patch|patch.dict()]] ''临时'' [[../sys#sys|sys.modules]] 中放置一个模拟。 此补丁处于活动状态时的任何导入都将获取模拟。 当补丁完成时(装饰函数退出,with 语句体完成或 <code>patcher.stop()</code> 被调用),那么之前的任何内容都将安全恢复。
in [[../sys#sys|<code>sys.modules</code>]]. Any imports whilst this patch is active will fetch the mock.
 
When the patch is complete (the decorated function exits, the with statement
 
body is complete or <code>patcher.stop()</code> is called) then whatever was there
 
previously will be restored safely.
 
  
Here's an example that mocks out the 'fooble' module.
+
这是一个模拟“fooble”模块的示例。
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,593行: 第1,368行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; import sys
+
<syntaxhighlight lang="python">>>> import sys
&gt;&gt;&gt; mock = Mock()
+
>>> mock = Mock()
&gt;&gt;&gt; with patch.dict('sys.modules', {'fooble': mock}):
+
>>> with patch.dict('sys.modules', {'fooble': mock}):
 
...    import fooble
 
...    import fooble
 
...    fooble.blob()
 
...    fooble.blob()
 
...
 
...
&lt;Mock name='mock.blob()' id='...'&gt;
+
<Mock name='mock.blob()' id='...'>
&gt;&gt;&gt; assert 'fooble' not in sys.modules
+
>>> assert 'fooble' not in sys.modules
&gt;&gt;&gt; mock.blob.assert_called_once_with()</pre>
+
>>> mock.blob.assert_called_once_with()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
As you can see the <code>import fooble</code> succeeds, but on exit there is no 'fooble'
+
如您所见,<code>import fooble</code> 成功,但退出时 [[../sys#sys|sys.modules]] 中没有“傻瓜”。
left in [[../sys#sys|<code>sys.modules</code>]].
 
  
This also works for the <code>from module import name</code> form:
+
这也适用于 <code>from module import name</code> 形式:
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,615行: 第1,389行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock()
+
<syntaxhighlight lang="python">>>> mock = Mock()
&gt;&gt;&gt; with patch.dict('sys.modules', {'fooble': mock}):
+
>>> with patch.dict('sys.modules', {'fooble': mock}):
 
...    from fooble import blob
 
...    from fooble import blob
 
...    blob.blip()
 
...    blob.blip()
 
...
 
...
&lt;Mock name='mock.blob.blip()' id='...'&gt;
+
<Mock name='mock.blob.blip()' id='...'>
&gt;&gt;&gt; mock.blob.blip.assert_called_once_with()</pre>
+
>>> mock.blob.blip.assert_called_once_with()</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
With slightly more work you can also mock package imports:
+
稍微多做一点工作,您还可以模拟包导入:
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,632行: 第1,406行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock = Mock()
+
<syntaxhighlight lang="python">>>> mock = Mock()
&gt;&gt;&gt; modules = {'package': mock, 'package.module': mock.module}
+
>>> modules = {'package': mock, 'package.module': mock.module}
&gt;&gt;&gt; with patch.dict('sys.modules', modules):
+
>>> with patch.dict('sys.modules', modules):
 
...    from package.module import fooble
 
...    from package.module import fooble
 
...    fooble()
 
...    fooble()
 
...
 
...
&lt;Mock name='mock.module.fooble()' id='...'&gt;
+
<Mock name='mock.module.fooble()' id='...'>
&gt;&gt;&gt; mock.module.fooble.assert_called_once_with()</pre>
+
>>> mock.module.fooble.assert_called_once_with()</syntaxhighlight>
  
 
</div>
 
</div>
第1,648行: 第1,422行:
 
<div id="tracking-order-of-calls-and-less-verbose-call-assertions" class="section">
 
<div id="tracking-order-of-calls-and-less-verbose-call-assertions" class="section">
  
=== Tracking order of calls and less verbose call assertions ===
+
=== 跟踪调用顺序和不那么冗长的调用断言 ===
  
The [[../unittest.mock#unittest.mock|<code>Mock</code>]] class allows you to track the ''order'' of method calls on
+
[[../unittest.mock#unittest.mock|Mock]] 类允许您通过 [[../unittest.mock#unittest.mock.Mock|method_calls]] 属性跟踪模拟对象上方法调用的 ''order''。 这不允许您跟踪单独模拟对象之间的调用顺序,但是我们可以使用 [[../unittest.mock#unittest.mock.Mock|mock_calls]] 来实现相同的效果。
your mock objects through the [[../unittest.mock#unittest.mock.Mock|<code>method_calls</code>]] attribute. This
 
doesn't allow you to track the order of calls between separate mock objects,
 
however we can use [[../unittest.mock#unittest.mock.Mock|<code>mock_calls</code>]] to achieve the same effect.
 
  
Because mocks track calls to child mocks in <code>mock_calls</code>, and accessing an
+
因为模拟跟踪对 <code>mock_calls</code> 中子模拟的调用,并且访问模拟的任意属性会创建一个子模拟,所以我们可以从父模拟创建单独的模拟。 对这些子模拟的调用将按顺序记录在父级的 <code>mock_calls</code> 中:
arbitrary attribute of a mock creates a child mock, we can create our separate
 
mocks from a parent one. Calls to those child mock will then all be recorded,
 
in order, in the <code>mock_calls</code> of the parent:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,664行: 第1,432行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; manager = Mock()
+
<syntaxhighlight lang="python">>>> manager = Mock()
&gt;&gt;&gt; mock_foo = manager.foo
+
>>> mock_foo = manager.foo
&gt;&gt;&gt; mock_bar = manager.bar</pre>
+
>>> mock_bar = manager.bar</syntaxhighlight>
  
 
</div>
 
</div>
第1,675行: 第1,443行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; mock_foo.something()
+
<syntaxhighlight lang="python">>>> mock_foo.something()
&lt;Mock name='mock.foo.something()' id='...'&gt;
+
<Mock name='mock.foo.something()' id='...'>
&gt;&gt;&gt; mock_bar.other.thing()
+
>>> mock_bar.other.thing()
&lt;Mock name='mock.bar.other.thing()' id='...'&gt;</pre>
+
<Mock name='mock.bar.other.thing()' id='...'></syntaxhighlight>
  
 
</div>
 
</div>
第1,687行: 第1,455行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; manager.mock_calls
+
<syntaxhighlight lang="python">>>> manager.mock_calls
[call.foo.something(), call.bar.other.thing()]</pre>
+
[call.foo.something(), call.bar.other.thing()]</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
We can then assert about the calls, including the order, by comparing with
+
然后,我们可以通过与管理器模拟上的 <code>mock_calls</code> 属性进行比较来断言调用,包括顺序:
the <code>mock_calls</code> attribute on the manager mock:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,700行: 第1,467行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; expected_calls = [call.foo.something(), call.bar.other.thing()]
+
<syntaxhighlight lang="python">>>> expected_calls = [call.foo.something(), call.bar.other.thing()]
&gt;&gt;&gt; manager.mock_calls == expected_calls
+
>>> manager.mock_calls == expected_calls
True</pre>
+
True</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If <code>patch</code> is creating, and putting in place, your mocks then you can attach
+
如果 <code>patch</code> 正在创建并放置您的模拟,那么您可以使用 [[../unittest.mock#unittest.mock.Mock|attach_mock()]] 方法将它们附加到管理器模拟。 挂接电话后会记录在管理员的<code>mock_calls</code>中。
them to a manager mock using the [[../unittest.mock#unittest.mock.Mock|<code>attach_mock()</code>]] method. After
 
attaching calls will be recorded in <code>mock_calls</code> of the manager.
 
  
 
<div class="highlight-python3 notranslate">
 
<div class="highlight-python3 notranslate">
第1,715行: 第1,480行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; manager = MagicMock()
+
<syntaxhighlight lang="python3">>>> manager = MagicMock()
&gt;&gt;&gt; with patch('mymodule.Class1') as MockClass1:
+
>>> with patch('mymodule.Class1') as MockClass1:
 
...    with patch('mymodule.Class2') as MockClass2:
 
...    with patch('mymodule.Class2') as MockClass2:
 
...        manager.attach_mock(MockClass1, 'MockClass1')
 
...        manager.attach_mock(MockClass1, 'MockClass1')
第1,722行: 第1,487行:
 
...        MockClass1().foo()
 
...        MockClass1().foo()
 
...        MockClass2().bar()
 
...        MockClass2().bar()
&lt;MagicMock name='mock.MockClass1().foo()' id='...'&gt;
+
<MagicMock name='mock.MockClass1().foo()' id='...'>
&lt;MagicMock name='mock.MockClass2().bar()' id='...'&gt;
+
<MagicMock name='mock.MockClass2().bar()' id='...'>
&gt;&gt;&gt; manager.mock_calls
+
>>> manager.mock_calls
 
[call.MockClass1(),
 
[call.MockClass1(),
 
call.MockClass1().foo(),
 
call.MockClass1().foo(),
 
call.MockClass2(),
 
call.MockClass2(),
call.MockClass2().bar()]</pre>
+
call.MockClass2().bar()]</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
If many calls have been made, but you're only interested in a particular
+
如果进行了多次调用,但您只对它们的特定序列感兴趣,那么另一种方法是使用 [[../unittest.mock#unittest.mock.Mock|assert_has_calls()]] 方法。 这需要一个调用列表(用 [[../unittest.mock#unittest.mock|call]] 对象构造)。 如果该调用序列在 [[../unittest.mock#unittest.mock.Mock|mock_calls]] 中,则断言成功。
sequence of them then an alternative is to use the
 
[[../unittest.mock#unittest.mock.Mock|<code>assert_has_calls()</code>]] method. This takes a list of calls (constructed
 
with the [[../unittest.mock#unittest.mock|<code>call</code>]] object). If that sequence of calls are in
 
[[../unittest.mock#unittest.mock.Mock|<code>mock_calls</code>]] then the assert succeeds.
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,743行: 第1,504行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; m = MagicMock()
+
<syntaxhighlight lang="python">>>> m = MagicMock()
&gt;&gt;&gt; m().foo().bar().baz()
+
>>> m().foo().bar().baz()
&lt;MagicMock name='mock().foo().bar().baz()' id='...'&gt;
+
<MagicMock name='mock().foo().bar().baz()' id='...'>
&gt;&gt;&gt; m.one().two().three()
+
>>> m.one().two().three()
&lt;MagicMock name='mock.one().two().three()' id='...'&gt;
+
<MagicMock name='mock.one().two().three()' id='...'>
&gt;&gt;&gt; calls = call.one().two().three().call_list()
+
>>> calls = call.one().two().three().call_list()
&gt;&gt;&gt; m.assert_has_calls(calls)</pre>
+
>>> m.assert_has_calls(calls)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Even though the chained call <code>m.one().two().three()</code> aren't the only calls that
+
即使链接调用 <code>m.one().two().three()</code> 不是对模拟进行的唯一调用,断言仍然成功。
have been made to the mock, the assert still succeeds.
 
  
Sometimes a mock may have several calls made to it, and you are only interested
+
有时,模拟可能会对其进行多次调用,而您只对断言这些调用中的 ''some'' 感兴趣。 您甚至可能不关心订单。 在这种情况下,您可以将 <code>any_order=True</code> 传递给 <code>assert_has_calls</code>
in asserting about ''some'' of those calls. You may not even care about the
 
order. In this case you can pass <code>any_order=True</code> to <code>assert_has_calls</code>:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,765行: 第1,523行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; m = MagicMock()
+
<syntaxhighlight lang="python">>>> m = MagicMock()
&gt;&gt;&gt; m(1), m.two(2, 3), m.seven(7), m.fifty('50')
+
>>> m(1), m.two(2, 3), m.seven(7), m.fifty('50')
 
(...)
 
(...)
&gt;&gt;&gt; calls = [call.fifty('50'), call(1), call.seven(7)]
+
>>> calls = [call.fifty('50'), call(1), call.seven(7)]
&gt;&gt;&gt; m.assert_has_calls(calls, any_order=True)</pre>
+
>>> m.assert_has_calls(calls, any_order=True)</syntaxhighlight>
  
 
</div>
 
</div>
第1,778行: 第1,536行:
 
<div id="more-complex-argument-matching" class="section">
 
<div id="more-complex-argument-matching" class="section">
  
=== More complex argument matching ===
+
=== 更复杂的参数匹配 ===
  
Using the same basic concept as [[../unittest.mock#unittest.mock|<code>ANY</code>]] we can implement matchers to do more
+
使用与 [[../unittest.mock#unittest.mock|ANY]] 相同的基本概念,我们可以实现匹配器对用作模拟参数的对象进行更复杂的断言。
complex assertions on objects used as arguments to mocks.
 
  
Suppose we expect some object to be passed to a mock that by default
+
假设我们希望将某个对象传递给一个模拟,该模拟默认情况下根据对象标识进行比较(这是用户定义类的 Python 默认值)。 要使用 [[../unittest.mock#unittest.mock.Mock|assert_called_with()]] 我们需要传入完全相同的对象。 如果我们只对这个对象的某些属性感兴趣,那么我们可以创建一个匹配器来为我们检查这些属性。
compares equal based on object identity (which is the Python default for user
 
defined classes). To use [[../unittest.mock#unittest.mock.Mock|<code>assert_called_with()</code>]] we would need to pass
 
in the exact same object. If we are only interested in some of the attributes
 
of this object then we can create a matcher that will check these attributes
 
for us.
 
  
You can see in this example how a 'standard' call to <code>assert_called_with</code> isn't
+
您可以在此示例中看到对 <code>assert_called_with</code> 的“标准”调用是不够的:
sufficient:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,797行: 第1,548行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class Foo:
+
<syntaxhighlight lang="python">>>> class Foo:
 
...    def __init__(self, a, b):
 
...    def __init__(self, a, b):
 
...        self.a, self.b = a, b
 
...        self.a, self.b = a, b
 
...
 
...
&gt;&gt;&gt; mock = Mock(return_value=None)
+
>>> mock = Mock(return_value=None)
&gt;&gt;&gt; mock(Foo(1, 2))
+
>>> mock(Foo(1, 2))
&gt;&gt;&gt; mock.assert_called_with(Foo(1, 2))
+
>>> mock.assert_called_with(Foo(1, 2))
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
     ...
 
     ...
AssertionError: Expected: call(&lt;__main__.Foo object at 0x...&gt;)
+
AssertionError: Expected: call(<__main__.Foo object at 0x...>)
Actual call: call(&lt;__main__.Foo object at 0x...&gt;)</pre>
+
Actual call: call(<__main__.Foo object at 0x...>)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
A comparison function for our <code>Foo</code> class might look something like this:
+
我们的 <code>Foo</code> 类的比较函数可能如下所示:
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,818行: 第1,569行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; def compare(self, other):
+
<syntaxhighlight lang="python">>>> def compare(self, other):
 
...    if not type(self) == type(other):
 
...    if not type(self) == type(other):
 
...        return False
 
...        return False
第1,826行: 第1,577行:
 
...        return False
 
...        return False
 
...    return True
 
...    return True
...</pre>
+
...</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
And a matcher object that can use comparison functions like this for its
+
一个可以使用这样的比较函数进行相等运算的匹配器对象看起来像这样:
equality operation would look something like this:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,838行: 第1,588行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; class Matcher:
+
<syntaxhighlight lang="python">>>> class Matcher:
 
...    def __init__(self, compare, some_obj):
 
...    def __init__(self, compare, some_obj):
 
...        self.compare = compare
 
...        self.compare = compare
第1,844行: 第1,594行:
 
...    def __eq__(self, other):
 
...    def __eq__(self, other):
 
...        return self.compare(self.some_obj, other)
 
...        return self.compare(self.some_obj, other)
...</pre>
+
...</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
Putting all this together:
+
将所有这些放在一起:
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,855行: 第1,605行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; match_foo = Matcher(compare, Foo(1, 2))
+
<syntaxhighlight lang="python">>>> match_foo = Matcher(compare, Foo(1, 2))
&gt;&gt;&gt; mock.assert_called_with(match_foo)</pre>
+
>>> mock.assert_called_with(match_foo)</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
The <code>Matcher</code> is instantiated with our compare function and the <code>Foo</code> object
+
<code>Matcher</code> 用我们的比较函数和我们想要比较的 <code>Foo</code> 对象实例化。 在 <code>assert_called_with</code> 中,将调用 <code>Matcher</code> 相等方法,它将调用模拟的对象与我们创建匹配器的对象进行比较。 如果它们匹配,则 <code>assert_called_with</code> 通过,如果它们不匹配,则引发 [[../exceptions#AssertionError|AssertionError]]
we want to compare against. In <code>assert_called_with</code> the <code>Matcher</code> equality
 
method will be called, which compares the object the mock was called with
 
against the one we created our matcher with. If they match then
 
<code>assert_called_with</code> passes, and if they don't an [[../exceptions#AssertionError|<code>AssertionError</code>]] is raised:
 
  
 
<div class="doctest highlight-default notranslate">
 
<div class="doctest highlight-default notranslate">
第1,871行: 第1,617行:
 
<div class="highlight">
 
<div class="highlight">
  
<pre>&gt;&gt;&gt; match_wrong = Matcher(compare, Foo(3, 4))
+
<syntaxhighlight lang="python">>>> match_wrong = Matcher(compare, Foo(3, 4))
&gt;&gt;&gt; mock.assert_called_with(match_wrong)
+
>>> mock.assert_called_with(match_wrong)
 
Traceback (most recent call last):
 
Traceback (most recent call last):
 
     ...
 
     ...
AssertionError: Expected: ((&lt;Matcher object at 0x...&gt;,), {})
+
AssertionError: Expected: ((<Matcher object at 0x...>,), {})
Called with: ((&lt;Foo object at 0x...&gt;,), {})</pre>
+
Called with: ((<Foo object at 0x...>,), {})</syntaxhighlight>
  
 
</div>
 
</div>
  
 
</div>
 
</div>
With a bit of tweaking you could have the comparison function raise the
+
通过一些调整,您可以让比较函数直接引发 [[../exceptions#AssertionError|AssertionError]] 并提供更有用的失败消息。
[[../exceptions#AssertionError|<code>AssertionError</code>]] directly and provide a more useful failure message.
 
  
As of version 1.5, the Python testing library [https://pyhamcrest.readthedocs.io/ PyHamcrest] provides similar functionality,
+
1.5 版开始,Python 测试库 [https://pyhamcrest.readthedocs.io/ PyHamcrest] 以相等匹配器 ([https://pyhamcrest.readthedocs.io/en/release-1.8/integration/#module-hamcrest.library.integration.match_equality hamcrest.library.integration.match_equality]) 的形式提供了类似的功能,在这里可能很有用。
that may be useful here, in the form of its equality matcher
 
([https://pyhamcrest.readthedocs.io/en/release-1.8/integration/#module-hamcrest.library.integration.match_equality hamcrest.library.integration.match_equality]).
 
  
  
第1,894行: 第1,637行:
  
 
</div>
 
</div>
 +
<div class="clearer">
  
[[Category:Python 3.9 中文文档]]
+
 
 +
 
 +
</div>
 +
 
 +
[[Category:Python 3.9 文档]]

2021年10月31日 (日) 04:54的最新版本

unittest.mock — 入门

3.3 版中的新功能。


使用模拟

模拟修补方法

Mock 对象的常见用途包括:

  • 修补方法
  • 记录对象的方法调用

您可能想要替换对象上的方法以检查系统的另一部分是否使用正确的参数调用它:

>>> real = SomeClass()
>>> real.method = MagicMock(name='method')
>>> real.method(3, 4, 5, key='value')
<MagicMock name='method()' id='...'>

一旦使用了我们的模拟(在本例中为 real.method),它就会有一些方法和属性,可以让你断言它是如何被使用的。

笔记

在大多数这些示例中,MockMagicMock 类是可以互换的。 由于 MagicMock 是功能更强大的类,因此默认情况下使用它是一个明智的选择。


一旦模拟被调用,它的 called 属性被设置为 True。 更重要的是,我们可以使用 assert_called_with()assert_Called_once_with() 方法来检查它是否使用正确的参数被调用。

此示例测试调用 ProductionClass().method 会导致调用 something 方法:

>>> class ProductionClass:
...     def method(self):
...         self.something(1, 2, 3)
...     def something(self, a, b, c):
...         pass
...
>>> real = ProductionClass()
>>> real.something = MagicMock()
>>> real.method()
>>> real.something.assert_called_once_with(1, 2, 3)

模拟对象上的方法调用

在上一个示例中,我们直接在对象上修补了一个方法以检查它是否被正确调用。 另一个常见的用例是将一个对象传递给一个方法(或被测系统的某个部分),然后检查它是否以正确的方式使用。

下面简单的ProductionClass有一个closer方法。 如果它被一个对象调用,那么它会在它上面调用 close

>>> class ProductionClass:
...     def closer(self, something):
...         something.close()
...

所以为了测试它,我们需要使用 close 方法传入一个对象并检查它是否被正确调用。

>>> real = ProductionClass()
>>> mock = Mock()
>>> real.closer(mock)
>>> mock.close.assert_called_with()

我们不需要做任何工作来在我们的模拟上提供 'close' 方法。 访问 close 创建它。 因此,如果 'close' 尚未被调用,则在测试中访问它会创建它,但 assert_Called_with() 将引发失败异常。


模拟类

一个常见的用例是模拟由您的被测代码实例化的类。 当您修补一个类时,该类将被替换为模拟。 实例是通过 调用类 创建的。 这意味着您可以通过查看模拟类的返回值来访问“模拟实例”。

在下面的例子中,我们有一个函数 some_function 实例化 Foo 并调用它的方法。 对 patch() 的调用用模拟替换了类 FooFoo实例是调用mock的结果,所以通过修改mockreturn_value来配置。

>>> def some_function():
...     instance = module.Foo()
...     return instance.method()
...
>>> with patch('module.Foo') as mock:
...     instance = mock.return_value
...     instance.method.return_value = 'the result'
...     result = some_function()
...     assert result == 'the result'

命名你的模拟

给你的模拟一个名字会很有用。 该名称显示在模拟的 repr 中,当模拟出现在测试失败消息中时会很有帮助。 该名称还传播到模拟的属性或方法:

>>> mock = MagicMock(name='foo')
>>> mock
<MagicMock name='foo' id='...'>
>>> mock.method
<MagicMock name='foo.method' id='...'>

跟踪所有呼叫

通常,您希望跟踪对方法的多次调用。 mock_calls 属性记录了对模拟的子属性的所有调用——以及对它们的子属性的调用。

>>> mock = MagicMock()
>>> mock.method()
<MagicMock name='mock.method()' id='...'>
>>> mock.attribute.method(10, x=53)
<MagicMock name='mock.attribute.method()' id='...'>
>>> mock.mock_calls
[call.method(), call.attribute.method(10, x=53)]

如果您对 mock_calls 进行断言并且调用了任何意外方法,则该断言将失败。 这很有用,因为除了断言您期望的调用已经进行之外,您还可以检查它们是否以正确的顺序进行并且没有额外的调用:

您使用 call 对象来构造列表以与 mock_calls 进行比较:

>>> expected = [call.method(), call.attribute.method(10, x=53)]
>>> mock.mock_calls == expected
True

但是,不会记录返回模拟的调用的参数,这意味着无法跟踪用于创建祖先的参数很重要的嵌套调用:

>>> m = Mock()
>>> m.factory(important=True).deliver()
<Mock name='mock.factory().deliver()' id='...'>
>>> m.mock_calls[-1] == call.factory(important=False).deliver()
True

设置返回值和属性

在模拟对象上设置返回值非常简单:

>>> mock = Mock()
>>> mock.return_value = 3
>>> mock()
3

当然,您可以对模拟上的方法执行相同的操作:

>>> mock = Mock()
>>> mock.method.return_value = 3
>>> mock.method()
3

返回值也可以在构造函数中设置:

>>> mock = Mock(return_value=3)
>>> mock()
3

如果您需要在模拟上设置属性,请执行以下操作:

>>> mock = Mock()
>>> mock.x = 3
>>> mock.x
3

有时您想模拟更复杂的情况,例如 mock.connection.cursor().execute("SELECT 1")。 如果我们希望这个调用返回一个列表,那么我们必须配置嵌套调用的结果。

我们可以使用 call 在像这样的“链式调用”中构造调用集,以便之后轻松断言:

>>> mock = Mock()
>>> cursor = mock.connection.cursor.return_value
>>> cursor.execute.return_value = ['foo']
>>> mock.connection.cursor().execute("SELECT 1")
['foo']
>>> expected = call.connection.cursor().execute("SELECT 1").call_list()
>>> mock.mock_calls
[call.connection.cursor(), call.connection.cursor().execute('SELECT 1')]
>>> mock.mock_calls == expected
True

正是对 .call_list() 的调用将我们的调用对象转换为代表链接调用的调用列表。


使用模拟引发异常

一个有用的属性是 side_effect。 如果将此设置为异常类或实例,则在调用模拟时将引发异常。

>>> mock = Mock(side_effect=Exception('Boom!'))
>>> mock()
Traceback (most recent call last):
  ...
Exception: Boom!

副作用函数和迭代

side_effect 也可以设置为一个函数或一个可迭代对象。 side_effect 作为可迭代对象的用例是您的模拟将被多次调用,并且您希望每次调用都返回不同的值。 当您将 side_effect 设置为可迭代对象时,每次调用模拟都会返回可迭代对象的下一个值:

>>> mock = MagicMock(side_effect=[4, 5, 6])
>>> mock()
4
>>> mock()
5
>>> mock()
6

对于更高级的用例,例如根据调用模拟的内容动态改变返回值,side_effect 可以是一个函数。 将使用与模拟相同的参数调用该函数。 无论函数返回什么,调用都会返回什么:

>>> vals = {(1, 2): 1, (2, 3): 2}
>>> def side_effect(*args):
...     return vals[args]
...
>>> mock = MagicMock(side_effect=side_effect)
>>> mock(1, 2)
1
>>> mock(2, 3)
2

模拟异步迭代器

从 Python 3.8 开始,AsyncMockMagicMock 支持通过 __aiter__ 模拟 Asynchronous Iterators__aiter__return_value 属性可用于设置要用于迭代的返回值。

>>> mock = MagicMock()  # AsyncMock also works here
>>> mock.__aiter__.return_value = [1, 2, 3]
>>> async def main():
...     return [i async for i in mock]
...
>>> asyncio.run(main())
[1, 2, 3]

模拟异步上下文管理器

从 Python 3.8 开始,AsyncMockMagicMock 支持通过 __aenter____aexit__ 模拟 异步上下文管理器。 默认情况下,__aenter____aexit__ 是返回异步函数的 AsyncMock 实例。

>>> class AsyncContextManager:
...     async def __aenter__(self):
...         return self
...     async def __aexit__(self, exc_type, exc, tb):
...         pass
...
>>> mock_instance = MagicMock(AsyncContextManager())  # AsyncMock also works here
>>> async def main():
...     async with mock_instance as result:
...         pass
...
>>> asyncio.run(main())
>>> mock_instance.__aenter__.assert_awaited_once()
>>> mock_instance.__aexit__.assert_awaited_once()

从现有对象创建模拟

过度使用模拟的一个问题是它将您的测试与模拟的实现相结合,而不是您的真实代码。 假设您有一个实现 some_method 的类。 在另一个类的测试中,您提供了 提供 some_method 的此对象的模拟。 如果稍后您重构第一个类,使其不再具有 some_method - 那么即使您的代码现在已损坏,您的测试仍将继续通过!

Mock 允许您提供一个对象作为模拟的规范,使用 spec 关键字参数。 访问您的规范对象上不存在的模拟上的方法/属性将立即引发属性错误。 如果您更改规范的实现,则使用该类的测试将立即开始失败,而无需在这些测试中实例化该类。

>>> mock = Mock(spec=SomeClass)
>>> mock.old_method()
Traceback (most recent call last):
   ...
AttributeError: object has no attribute 'old_method'

使用规范还可以更智能地匹配对模拟的调用,无论某些参数是作为位置参数还是命名参数传递的:

>>> def f(a, b, c): pass
...
>>> mock = Mock(spec=f)
>>> mock(1, 2, 3)
<Mock name='mock()' id='140161580456576'>
>>> mock.assert_called_with(a=1, b=2, c=3)

如果您希望这种更智能的匹配也适用于模拟上的方法调用,您可以使用 auto-speccing

如果您想要一种更强大的规范形式来防止设置任意属性以及获取它们,那么您可以使用 spec_set 而不是 spec


补丁装饰器

笔记

使用 patch() 重要的是您在查找它们的命名空间中修补对象。 这通常很简单,但要获得快速指南,请阅读 在哪里打补丁


测试中的一个常见需求是修补类属性或模块属性,例如修补内置模块或修补模块中的类以测试它是否已实例化。 模块和类实际上是全局的,因此必须在测试后取消对它们的修补,否则修补程序将持续存在于其他测试中并导致难以诊断的问题。

mock 为此提供了三个方便的装饰器:patch()patch.object()patch.dict()patch 采用单个字符串,形式为 package.module.Class.attribute 来指定您正在修补的属性。 它还可选地采用您希望替换属性(或类或其他)的值。 'patch.object' 接受一个对象和你想要修补的属性的名称,加上可选的值来修补它。

patch.object

>>> original = SomeClass.attribute
>>> @patch.object(SomeClass, 'attribute', sentinel.attribute)
... def test():
...     assert SomeClass.attribute == sentinel.attribute
...
>>> test()
>>> assert SomeClass.attribute == original

>>> @patch('package.module.attribute', sentinel.attribute)
... def test():
...     from package.module import attribute
...     assert attribute is sentinel.attribute
...
>>> test()

如果您正在修补模块(包括 builtins),则使用 patch() 而不是 patch.object()

>>> mock = MagicMock(return_value=sentinel.file_handle)
>>> with patch('builtins.open', mock):
...     handle = open('filename', 'r')
...
>>> mock.assert_called_with('filename', 'r')
>>> assert handle == sentinel.file_handle, "incorrect file handle returned"

如果需要,模块名称可以“带点”,格式为 package.module

>>> @patch('package.module.ClassName.attribute', sentinel.attribute)
... def test():
...     from package.module import ClassName
...     assert ClassName.attribute == sentinel.attribute
...
>>> test()

一个不错的模式是实际装饰测试方法本身:

>>> class MyTest(unittest.TestCase):
...     @patch.object(SomeClass, 'attribute', sentinel.attribute)
...     def test_something(self):
...         self.assertEqual(SomeClass.attribute, sentinel.attribute)
...
>>> original = SomeClass.attribute
>>> MyTest('test_something').test_something()
>>> assert SomeClass.attribute == original

如果你想用一个 Mock 打补丁,你可以使用只有一个参数的 patch() (或带有两个参数的 patch.object() )。 将为您创建模拟并传递给测试函数/方法:

>>> class MyTest(unittest.TestCase):
...     @patch.object(SomeClass, 'static_method')
...     def test_something(self, mock_method):
...         SomeClass.static_method()
...         mock_method.assert_called_with()
...
>>> MyTest('test_something').test_something()

您可以使用此模式堆叠多个补丁装饰器:

>>> class MyTest(unittest.TestCase):
...     @patch('package.module.ClassName1')
...     @patch('package.module.ClassName2')
...     def test_something(self, MockClass2, MockClass1):
...         self.assertIs(package.module.ClassName1, MockClass1)
...         self.assertIs(package.module.ClassName2, MockClass2)
...
>>> MyTest('test_something').test_something()

当您嵌套补丁装饰器时,模拟以它们应用的相同顺序传递给装饰函数(应用装饰器的正常 Python 顺序)。 这意味着自下而上,因此在上面的示例中,首先传入 test_module.ClassName2 的模拟。

还有 patch.dict() 用于在范围内设置字典中的值,并在测试结束时将字典恢复到其原始状态:

>>> foo = {'key': 'value'}
>>> original = foo.copy()
>>> with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
...     assert foo == {'newkey': 'newvalue'}
...
>>> assert foo == original

patchpatch.objectpatch.dict 都可以用作上下文管理器。

在您使用 patch() 为您创建模拟的地方,您可以使用 with 语句的“as”形式获取对模拟的引用:

>>> class ProductionClass:
...     def method(self):
...         pass
...
>>> with patch.object(ProductionClass, 'method') as mock_method:
...     mock_method.return_value = None
...     real = ProductionClass()
...     real.method(1, 2, 3)
...
>>> mock_method.assert_called_with(1, 2, 3)

作为替代 patchpatch.objectpatch.dict 可以用作类装饰器。 以这种方式使用时,它与将装饰器单独应用于名称以“test”开头的每个方法相同。


进一步的例子

下面是一些稍微更高级的场景的更多示例。

模拟链式调用

一旦你理解了 return_value 属性,模拟链调用实际上很简单。 当第一次调用模拟时,或者在调用之前获取它的 return_value 时,会创建一个新的 Mock

这意味着您可以通过询问 return_value 模拟来了解从调用模拟对象返回的对象是如何被使用的:

>>> mock = Mock()
>>> mock().foo(a=2, b=3)
<Mock name='mock().foo()' id='...'>
>>> mock.return_value.foo.assert_called_with(a=2, b=3)

从这里开始,这是一个简单的配置步骤,然后对链式调用进行断言。 当然,另一种选择是首先以更可测试的方式编写代码……

所以,假设我们有一些看起来像这样的代码:

>>> class Something:
...     def __init__(self):
...         self.backend = BackendProvider()
...     def method(self):
...         response = self.backend.get_endpoint('foobar').create_call('spam', 'eggs').start_call()
...         # more code

假设BackendProvider已经测试好了,我们如何测试method()? 具体来说,我们要测试代码段 # more code 是否以正确的方式使用响应对象。

由于此调用链是从实例属性进行的,我们可以在 Something 实例上对 backend 属性进行猴子修补。 在这种特殊情况下,我们只对最终调用 start_call 的返回值感兴趣,因此我们没有太多配置要做。 让我们假设它返回的对象是“类文件”,因此我们将确保我们的响应对象使用内置的 open() 作为其 spec

为此,我们创建一个模拟实例作为我们的模拟后端,并为其创建一个模拟响应对象。 要将响应设置为最终 start_call 的返回值,我们可以这样做:

mock_backend.get_endpoint.return_value.create_call.return_value.start_call.return_value = mock_response

我们可以使用 configure_mock() 方法以更好的方式直接为我们设置返回值:

>>> something = Something()
>>> mock_response = Mock(spec=open)
>>> mock_backend = Mock()
>>> config = {'get_endpoint.return_value.create_call.return_value.start_call.return_value': mock_response}
>>> mock_backend.configure_mock(**config)

有了这些,我们就可以修补“模拟后端”,并可以进行真正的调用:

>>> something.backend = mock_backend
>>> something.method()

使用 mock_calls 我们可以使用单个断言检查链接的调用。 一个链式调用是一行代码中的多个调用,所以在mock_calls中会有多个条目。 我们可以使用 call.call_list() 为我们创建这个调用列表:

>>> chained = call.get_endpoint('foobar').create_call('spam', 'eggs').start_call()
>>> call_list = chained.call_list()
>>> assert mock_backend.mock_calls == call_list

部分嘲讽

在某些测试中,我想模拟对 datetime.date.today() 的调用以返回已知日期,但我不想阻止被测代码创建新的日期对象。 不幸的是 datetime.date 是用 C 编写的,所以我不能只是修补静态 date.today() 方法。

我找到了一种简单的方法来做到这一点,该方法涉及用模拟有效地包装日期类,但将构造函数的调用传递给真正的类(并返回真实的实例)。

patch 装饰器 用于模拟被测模块中的 date 类。 然后将模拟日期类的 side_effect 属性设置为返回真实日期的 lambda 函数。 当模拟日期类被调用时,side_effect 将构造并返回一个真实日期。

>>> from datetime import date
>>> with patch('mymodule.date') as mock_date:
...     mock_date.today.return_value = date(2010, 10, 8)
...     mock_date.side_effect = lambda *args, **kw: date(*args, **kw)
...
...     assert mymodule.date.today() == date(2010, 10, 8)
...     assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)

请注意,我们不会全局修补 datetime.date,而是在 使用 的模块中修补 date。 见在哪里打补丁

date.today() 被调用时,会返回一个已知日期,但对 date(...) 构造函数的调用仍然返回正常日期。 如果没有这个,你会发现自己必须使用与被测代码完全相同的算法来计算预期结果,这是一种经典的测试反模式。

对日期构造函数的调用记录在 mock_date 属性(call_count 和朋友)中,这也可能对您的测试有用。

这个博客条目 中讨论了另一种处理模拟日期或其他内置类的方法。


模拟生成器方法

Python 生成器是一个函数或方法,它使用 yield 语句在迭代 1 时返回一系列值。

调用生成器方法/函数以返回生成器对象。 然后迭代的是生成器对象。 迭代的协议方法是 __iter__(),所以我们可以使用 MagicMock 来模拟它。

这是一个示例类,其“iter”方法实现为生成器:

>>> class Foo:
...     def iter(self):
...         for i in [1, 2, 3]:
...             yield i
...
>>> foo = Foo()
>>> list(foo.iter())
[1, 2, 3]

我们将如何模拟这个类,尤其是它的“iter”方法?

要配置迭代返回的值(隐含在调用 list 中),我们需要配置调用返回的对象 foo.iter()

>>> mock_foo = MagicMock()
>>> mock_foo.iter.return_value = iter([1, 2, 3])
>>> list(mock_foo.iter())
[1, 2, 3]
1
还有生成器表达式和更多 生成器的高级用法 ,但我们在这里不关心它们。 对生成器及其强大功能的非常好的介绍:面向系统程序员的生成器技巧


对每个测试方法应用相同的补丁

如果您想为多个测试方法设置多个补丁,显而易见的方法是将补丁装饰器应用于每个方法。 这感觉像是不必要的重复。 对于 Python 2.6 或更新版本,您可以使用 patch()(以各种形式)作为类装饰器。 这将补丁应用于类上的所有测试方法。 测试方法由名称以 test 开头的方法标识:

>>> @patch('mymodule.SomeClass')
... class MyTest(unittest.TestCase):
...
...     def test_one(self, MockSomeClass):
...         self.assertIs(mymodule.SomeClass, MockSomeClass)
...
...     def test_two(self, MockSomeClass):
...         self.assertIs(mymodule.SomeClass, MockSomeClass)
...
...     def not_a_test(self):
...         return 'something'
...
>>> MyTest('test_one').test_one()
>>> MyTest('test_two').test_two()
>>> MyTest('test_two').not_a_test()
'something'

管理补丁的另一种方法是使用 补丁方法:start 和 stop。 这些允许您将补丁移动到 setUptearDown 方法中。

>>> class MyTest(unittest.TestCase):
...     def setUp(self):
...         self.patcher = patch('mymodule.foo')
...         self.mock_foo = self.patcher.start()
...
...     def test_foo(self):
...         self.assertIs(mymodule.foo, self.mock_foo)
...
...     def tearDown(self):
...         self.patcher.stop()
...
>>> MyTest('test_foo').run()

如果您使用这种技术,您必须通过调用 stop 来确保补丁被“撤销”。 这可能比您想象的要复杂,因为如果在 setUp 中引发异常,则不会调用 tearDown。 unittest.TestCase.addCleanup() 使这更容易:

>>> class MyTest(unittest.TestCase):
...     def setUp(self):
...         patcher = patch('mymodule.foo')
...         self.addCleanup(patcher.stop)
...         self.mock_foo = patcher.start()
...
...     def test_foo(self):
...         self.assertIs(mymodule.foo, self.mock_foo)
...
>>> MyTest('test_foo').run()

模拟未绑定的方法

在今天编写测试时,我需要修补一个 未绑定方法 (修补类上的方法而不是实例上的方法)。 我需要将 self 作为第一个参数传入,因为我想断言哪些对象正在调用此特定方法。 问题是您无法为此使用模拟进行修补,因为如果您用模拟替换未绑定的方法,则从实例中获取时它不会成为绑定方法,因此不会自我传入。 解决方法是用一个真正的函数来修补未绑定的方法。 patch() 装饰器使得用模拟修补方法变得如此简单,以至于必须创建一个真正的函数变得很麻烦。

如果您将 autospec=True 传递给 patch,那么它会使用 real 函数对象进行修补。 这个函数对象与它正在替换的函数对象具有相同的签名,但在幕后委托给一个模拟。 您仍然以与以前完全相同的方式自动创建模拟。 但这意味着,如果您使用它来修补类上的未绑定方法,则如果从实例中获取模拟函数,则该模拟函数将转换为绑定方法。 它将 self 作为第一个参数传入,这正是我想要的:

>>> class Foo:
...   def foo(self):
...     pass
...
>>> with patch.object(Foo, 'foo', autospec=True) as mock_foo:
...   mock_foo.return_value = 'foo'
...   foo = Foo()
...   foo.foo()
...
'foo'
>>> mock_foo.assert_called_once_with(foo)

如果我们不使用 autospec=True,那么未绑定的方法会用 Mock 实例修补,而不是用 self 调用。


使用模拟检查多个调用

mock 有一个很好的 API 来断言你的模拟对象是如何使用的。

>>> mock = Mock()
>>> mock.foo_bar.return_value = None
>>> mock.foo_bar('baz', spam='eggs')
>>> mock.foo_bar.assert_called_with('baz', spam='eggs')

如果您的模拟只被调用一次,您可以使用 assert_called_once_with() 方法,该方法也断言 call_count 是一个。

>>> mock.foo_bar.assert_called_once_with('baz', spam='eggs')
>>> mock.foo_bar()
>>> mock.foo_bar.assert_called_once_with('baz', spam='eggs')
Traceback (most recent call last):
    ...
AssertionError: Expected to be called once. Called 2 times.

assert_called_withassert_called_once_with 都对 最近的 调用做出断言。 如果您的模拟将被多次调用,并且您想对 all 这些调用进行断言,您可以使用 call_args_list

>>> mock = Mock(return_value=None)
>>> mock(1, 2, 3)
>>> mock(4, 5, 6)
>>> mock()
>>> mock.call_args_list
[call(1, 2, 3), call(4, 5, 6), call()]

call 助手可以轻松地对这些调用进行断言。 您可以建立一个预期调用列表并将其与 call_args_list 进行比较。 这看起来与 call_args_list 的再现非常相似:

>>> expected = [call(1, 2, 3), call(4, 5, 6), call()]
>>> mock.call_args_list == expected
True

处理可变参数

另一种情况很少见,但会咬你,就是当你的模拟被可变参数调用时。 call_argscall_args_list 存储对参数的 引用 。 如果参数被测试中的代码改变了,那么你不能再断言调用模拟时的值是什么。

这是一些显示问题的示例代码。 想象一下在“mymodule”中定义的以下函数:

def frob(val):
    pass

def grob(val):
    "First frob and then clear val"
    frob(val)
    val.clear()

当我们尝试测试 grob 使用正确的参数调用 frob 时,看看会发生什么:

>>> with patch('mymodule.frob') as mock_frob:
...     val = {6}
...     mymodule.grob(val)
...
>>> val
set()
>>> mock_frob.assert_called_with({6})
Traceback (most recent call last):
    ...
AssertionError: Expected: (({6},), {})
Called with: ((set(),), {})

一种可能性是模拟复制您传入的参数。 如果你做的断言依赖于对象身份的相等性,那么这可能会导致问题。

这是使用 side_effect 功能的一种解决方案。 如果您为模拟提供 side_effect 函数,则将使用与模拟相同的参数调用 side_effect。 这使我们有机会复制参数并将它们存储以供以后的断言使用。 在这个例子中,我使用 another 模拟来存储参数,以便我可以使用模拟方法进行断言。 一个辅助函数再次为我设置了这个。

>>> from copy import deepcopy
>>> from unittest.mock import Mock, patch, DEFAULT
>>> def copy_call_args(mock):
...     new_mock = Mock()
...     def side_effect(*args, **kwargs):
...         args = deepcopy(args)
...         kwargs = deepcopy(kwargs)
...         new_mock(*args, **kwargs)
...         return DEFAULT
...     mock.side_effect = side_effect
...     return new_mock
...
>>> with patch('mymodule.frob') as mock_frob:
...     new_mock = copy_call_args(mock_frob)
...     val = {6}
...     mymodule.grob(val)
...
>>> new_mock.assert_called_with({6})
>>> new_mock.call_args
call({6})

copy_call_args 与将被调用的模拟一起调用。 它返回一个我们对其进行断言的新模拟。 side_effect 函数制作了 args 的副本,并使用副本调用我们的 new_mock

笔记

如果您的模拟只使用一次,则有一种更简单的方法可以在调用时检查参数。 您可以简单地在 side_effect 函数内部进行检查。

>>> def side_effect(arg):
...     assert arg == {6}
...
>>> mock = Mock(side_effect=side_effect)
>>> mock({6})
>>> mock(set())
Traceback (most recent call last):
    ...
AssertionError

另一种方法是创建一个 MockMagicMock 的子类来复制(使用 copy.deepcopy())参数。 这是一个示例实现:

>>> from copy import deepcopy
>>> class CopyingMock(MagicMock):
...     def __call__(self, /, *args, **kwargs):
...         args = deepcopy(args)
...         kwargs = deepcopy(kwargs)
...         return super().__call__(*args, **kwargs)
...
>>> c = CopyingMock(return_value=None)
>>> arg = set()
>>> c(arg)
>>> arg.add(1)
>>> c.assert_called_with(set())
>>> c.assert_called_with(arg)
Traceback (most recent call last):
    ...
AssertionError: Expected call: mock({1})
Actual call: mock(set())
>>> c.foo
<CopyingMock name='mock.foo' id='...'>

当您对 MockMagicMock 所有动态创建的属性进行子类化时,return_value 将自动使用您的子类。 这意味着 CopyingMock 的所有子代也将具有 CopyingMock 类型。


嵌套补丁

使用补丁作为上下文管理器很好,但如果你做多个补丁,你最终可能会嵌套使用越来越向右缩进的语句:

>>> class MyTest(unittest.TestCase):
...
...     def test_foo(self):
...         with patch('mymodule.Foo') as mock_foo:
...             with patch('mymodule.Bar') as mock_bar:
...                 with patch('mymodule.Spam') as mock_spam:
...                     assert mymodule.Foo is mock_foo
...                     assert mymodule.Bar is mock_bar
...                     assert mymodule.Spam is mock_spam
...
>>> original = mymodule.Foo
>>> MyTest('test_foo').test_foo()
>>> assert mymodule.Foo is original

使用 unittest cleanup 函数和 补丁方法:start 和 stop 我们可以在没有嵌套缩进的情况下实现相同的效果。 一个简单的辅助方法 create_patch 将补丁放置到位并为我们返回创建的模拟:

>>> class MyTest(unittest.TestCase):
...
...     def create_patch(self, name):
...         patcher = patch(name)
...         thing = patcher.start()
...         self.addCleanup(patcher.stop)
...         return thing
...
...     def test_foo(self):
...         mock_foo = self.create_patch('mymodule.Foo')
...         mock_bar = self.create_patch('mymodule.Bar')
...         mock_spam = self.create_patch('mymodule.Spam')
...
...         assert mymodule.Foo is mock_foo
...         assert mymodule.Bar is mock_bar
...         assert mymodule.Spam is mock_spam
...
>>> original = mymodule.Foo
>>> MyTest('test_foo').run()
>>> assert mymodule.Foo is original

用 MagicMock 模拟字典

你可能想要模拟一个字典或其他容器对象,记录对它的所有访问,同时让它仍然像字典一样运行。

我们可以使用 MagicMock 来做到这一点,它的行为类似于字典,并使用 side_effect 将字典访问委托给我们控制的真实底层字典。

当我们的 MagicMock__getitem__()__setitem__() 方法被调用(正常字典访问)时,side_effect 将使用键调用(在 [ X154X] 值太)。 我们还可以控制返回的内容。

使用 MagicMock 后,我们可以使用 call_args_list 等属性来断言字典的使用方式:

>>> my_dict = {'a': 1, 'b': 2, 'c': 3}
>>> def getitem(name):
...      return my_dict[name]
...
>>> def setitem(name, val):
...     my_dict[name] = val
...
>>> mock = MagicMock()
>>> mock.__getitem__.side_effect = getitem
>>> mock.__setitem__.side_effect = setitem

笔记

使用 MagicMock 的替代方法是使用 Mockonly 提供您特别想要的魔术方法:

>>> mock = Mock()
>>> mock.__getitem__ = Mock(side_effect=getitem)
>>> mock.__setitem__ = Mock(side_effect=setitem)

third 选项是使用 MagicMock 但传入 dict 作为 spec(或 spec_set)参数,以便MagicMock 创建的只有字典魔术方法可用:

>>> mock = MagicMock(spec_set=dict)
>>> mock.__getitem__.side_effect = getitem
>>> mock.__setitem__.side_effect = setitem

有了这些副作用功能,mock 将像普通字典一样运行,但会记录访问。 如果您尝试访问不存在的密钥,它甚至会引发 KeyError

>>> mock['a']
1
>>> mock['c']
3
>>> mock['d']
Traceback (most recent call last):
    ...
KeyError: 'd'
>>> mock['b'] = 'fish'
>>> mock['d'] = 'eggs'
>>> mock['b']
'fish'
>>> mock['d']
'eggs'

使用后,您可以使用正常的模拟方法和属性对访问进行断言:

>>> mock.__getitem__.call_args_list
[call('a'), call('c'), call('d'), call('b'), call('d')]
>>> mock.__setitem__.call_args_list
[call('b', 'fish'), call('d', 'eggs')]
>>> my_dict
{'a': 1, 'b': 'fish', 'c': 3, 'd': 'eggs'}

模拟子类及其属性

您可能想要子类化 Mock 的原因有很多。 原因之一可能是添加辅助方法。 这是一个愚蠢的例子:

>>> class MyMock(MagicMock):
...     def has_been_called(self):
...         return self.called
...
>>> mymock = MyMock(return_value=None)
>>> mymock
<MyMock id='...'>
>>> mymock.has_been_called()
False
>>> mymock()
>>> mymock.has_been_called()
True

Mock 实例的标准行为是属性和返回值模拟与访问它们的模拟具有相同的类型。 这确保 Mock 属性为 MocksMagicMock 属性为 MagicMocks 2。 因此,如果您要子类化以添加辅助方法,那么它们也将可用于您的子类实例的属性和返回值模拟。

>>> mymock.foo
<MyMock name='mock.foo' id='...'>
>>> mymock.foo.has_been_called()
False
>>> mymock.foo()
<MyMock name='mock.foo()' id='...'>
>>> mymock.foo.has_been_called()
True

有时这很不方便。 例如, 一个用户 正在子类化模拟以创建一个 扭曲适配器 。 将此应用于属性实际上会导致错误。

Mock(在其所有风格中)使用称为 _get_child_mock 的方法为属性和返回值创建这些“子模拟”。 您可以通过覆盖此方法来防止您的子类用于属性。 签名是它接受任意关键字参数(**kwargs),然后将其传递给模拟构造函数:

>>> class Subclass(MagicMock):
...     def _get_child_mock(self, /, **kwargs):
...         return MagicMock(**kwargs)
...
>>> mymock = Subclass()
>>> mymock.foo
<MagicMock name='mock.foo' id='...'>
>>> assert isinstance(mymock, Subclass)
>>> assert not isinstance(mymock.foo, Subclass)
>>> assert not isinstance(mymock(), Subclass)
2
此规则的一个例外是不可调用的模拟。 属性使用可调用变体,因为否则不可调用的模拟不能具有可调用的方法。


使用 patch.dict 模拟导入

模拟可能很难的一种情况是您在函数中具有本地导入。 这些更难模拟,因为它们没有使用我们可以修补的模块命名空间中的对象。

通常应避免本地进口。 有时这样做是为了防止循环依赖,为此 通常 有更好的方法来解决问题(重构代码)或通过延迟导入来防止“前期成本”。 这也可以通过比无条件本地导入更好的方式来解决(将模块存储为类或模块属性,并且仅在第一次使用时进行导入)。

除此之外,还有一种方法可以使用 mock 来影响导入的结果。 导入从 sys.modules 字典中获取一个 object。 请注意,它获取了一个 object,它不需要是一个模块。 第一次导入模块会导致模块对象被放入 sys.modules,所以通常当你导入某些东西时,你会得到一个模块。 然而,情况不必如此。

这意味着您可以使用 patch.dict()临时sys.modules 中放置一个模拟。 此补丁处于活动状态时的任何导入都将获取模拟。 当补丁完成时(装饰函数退出,with 语句体完成或 patcher.stop() 被调用),那么之前的任何内容都将安全恢复。

这是一个模拟“fooble”模块的示例。

>>> import sys
>>> mock = Mock()
>>> with patch.dict('sys.modules', {'fooble': mock}):
...    import fooble
...    fooble.blob()
...
<Mock name='mock.blob()' id='...'>
>>> assert 'fooble' not in sys.modules
>>> mock.blob.assert_called_once_with()

如您所见,import fooble 成功,但退出时 sys.modules 中没有“傻瓜”。

这也适用于 from module import name 形式:

>>> mock = Mock()
>>> with patch.dict('sys.modules', {'fooble': mock}):
...    from fooble import blob
...    blob.blip()
...
<Mock name='mock.blob.blip()' id='...'>
>>> mock.blob.blip.assert_called_once_with()

稍微多做一点工作,您还可以模拟包导入:

>>> mock = Mock()
>>> modules = {'package': mock, 'package.module': mock.module}
>>> with patch.dict('sys.modules', modules):
...    from package.module import fooble
...    fooble()
...
<Mock name='mock.module.fooble()' id='...'>
>>> mock.module.fooble.assert_called_once_with()

跟踪调用顺序和不那么冗长的调用断言

Mock 类允许您通过 method_calls 属性跟踪模拟对象上方法调用的 order。 这不允许您跟踪单独模拟对象之间的调用顺序,但是我们可以使用 mock_calls 来实现相同的效果。

因为模拟跟踪对 mock_calls 中子模拟的调用,并且访问模拟的任意属性会创建一个子模拟,所以我们可以从父模拟创建单独的模拟。 对这些子模拟的调用将按顺序记录在父级的 mock_calls 中:

>>> manager = Mock()
>>> mock_foo = manager.foo
>>> mock_bar = manager.bar
>>> mock_foo.something()
<Mock name='mock.foo.something()' id='...'>
>>> mock_bar.other.thing()
<Mock name='mock.bar.other.thing()' id='...'>
>>> manager.mock_calls
[call.foo.something(), call.bar.other.thing()]

然后,我们可以通过与管理器模拟上的 mock_calls 属性进行比较来断言调用,包括顺序:

>>> expected_calls = [call.foo.something(), call.bar.other.thing()]
>>> manager.mock_calls == expected_calls
True

如果 patch 正在创建并放置您的模拟,那么您可以使用 attach_mock() 方法将它们附加到管理器模拟。 挂接电话后会记录在管理员的mock_calls中。

>>> manager = MagicMock()
>>> with patch('mymodule.Class1') as MockClass1:
...     with patch('mymodule.Class2') as MockClass2:
...         manager.attach_mock(MockClass1, 'MockClass1')
...         manager.attach_mock(MockClass2, 'MockClass2')
...         MockClass1().foo()
...         MockClass2().bar()
<MagicMock name='mock.MockClass1().foo()' id='...'>
<MagicMock name='mock.MockClass2().bar()' id='...'>
>>> manager.mock_calls
[call.MockClass1(),
call.MockClass1().foo(),
call.MockClass2(),
call.MockClass2().bar()]

如果进行了多次调用,但您只对它们的特定序列感兴趣,那么另一种方法是使用 assert_has_calls() 方法。 这需要一个调用列表(用 call 对象构造)。 如果该调用序列在 mock_calls 中,则断言成功。

>>> m = MagicMock()
>>> m().foo().bar().baz()
<MagicMock name='mock().foo().bar().baz()' id='...'>
>>> m.one().two().three()
<MagicMock name='mock.one().two().three()' id='...'>
>>> calls = call.one().two().three().call_list()
>>> m.assert_has_calls(calls)

即使链接调用 m.one().two().three() 不是对模拟进行的唯一调用,断言仍然成功。

有时,模拟可能会对其进行多次调用,而您只对断言这些调用中的 some 感兴趣。 您甚至可能不关心订单。 在这种情况下,您可以将 any_order=True 传递给 assert_has_calls

>>> m = MagicMock()
>>> m(1), m.two(2, 3), m.seven(7), m.fifty('50')
(...)
>>> calls = [call.fifty('50'), call(1), call.seven(7)]
>>> m.assert_has_calls(calls, any_order=True)

更复杂的参数匹配

使用与 ANY 相同的基本概念,我们可以实现匹配器对用作模拟参数的对象进行更复杂的断言。

假设我们希望将某个对象传递给一个模拟,该模拟默认情况下根据对象标识进行比较(这是用户定义类的 Python 默认值)。 要使用 assert_called_with() 我们需要传入完全相同的对象。 如果我们只对这个对象的某些属性感兴趣,那么我们可以创建一个匹配器来为我们检查这些属性。

您可以在此示例中看到对 assert_called_with 的“标准”调用是不够的:

>>> class Foo:
...     def __init__(self, a, b):
...         self.a, self.b = a, b
...
>>> mock = Mock(return_value=None)
>>> mock(Foo(1, 2))
>>> mock.assert_called_with(Foo(1, 2))
Traceback (most recent call last):
    ...
AssertionError: Expected: call(<__main__.Foo object at 0x...>)
Actual call: call(<__main__.Foo object at 0x...>)

我们的 Foo 类的比较函数可能如下所示:

>>> def compare(self, other):
...     if not type(self) == type(other):
...         return False
...     if self.a != other.a:
...         return False
...     if self.b != other.b:
...         return False
...     return True
...

一个可以使用这样的比较函数进行相等运算的匹配器对象看起来像这样:

>>> class Matcher:
...     def __init__(self, compare, some_obj):
...         self.compare = compare
...         self.some_obj = some_obj
...     def __eq__(self, other):
...         return self.compare(self.some_obj, other)
...

将所有这些放在一起:

>>> match_foo = Matcher(compare, Foo(1, 2))
>>> mock.assert_called_with(match_foo)

Matcher 用我们的比较函数和我们想要比较的 Foo 对象实例化。 在 assert_called_with 中,将调用 Matcher 相等方法,它将调用模拟的对象与我们创建匹配器的对象进行比较。 如果它们匹配,则 assert_called_with 通过,如果它们不匹配,则引发 AssertionError

>>> match_wrong = Matcher(compare, Foo(3, 4))
>>> mock.assert_called_with(match_wrong)
Traceback (most recent call last):
    ...
AssertionError: Expected: ((<Matcher object at 0x...>,), {})
Called with: ((<Foo object at 0x...>,), {})

通过一些调整,您可以让比较函数直接引发 AssertionError 并提供更有用的失败消息。

从 1.5 版开始,Python 测试库 PyHamcrest 以相等匹配器 (hamcrest.library.integration.match_equality) 的形式提供了类似的功能,在这里可能很有用。