是否可以模拟 lambda 表达式?
Posted
技术标签:
【中文标题】是否可以模拟 lambda 表达式?【英文标题】:Is it possible to mock a lambda expression? 【发布时间】:2017-04-19 00:39:06 【问题描述】:前两个函数display_pane_1
和template_1
很容易测试
在方法test_1
中。我想将这两个函数重构为一个函数display_pane_2
。
lambdademo.py:
def display_pane_1():
display_register(template_1)
def template_1():
return 'hello mum'
def display_pane_2():
display_register(lambda: 'hello mum')
def display_register(template):
print(template())
test_lambdademo.py
import unittest
import unittest.mock as mock
import lambdademo
class TestLambda1(unittest.TestCase):
def setUp(self):
p = mock.patch('lambdademo.display_register')
self.mock_display_register = p.start()
self.addCleanup(p.stop)
def test_1(self):
lambdademo.display_pane_1()
self.mock_display_register.assert_called_with(lambdademo.template_1)
def test_2(self):
lambdademo.display_pane_2()
self.mock_display_register.assert_called_with('????????')
你能帮我为display_pane_2
写一个有效的测试吗?我想测试完整的 lambda 表达式,即 lambda x: 'hell mum'
应该失败。
我尝试了两种解决方法。
第一个选项是test_1
的简单副本,将lambdademo.template_1
的参数替换为lambda
的模拟。我在手册中找不到任何建议我应该如何模拟像 lambda 这样的表达式。
如果它在手册中,请告诉我在哪里。
我的第二个选择是在 Stack Overflow 和 Internet 上进行更广泛的搜索后得出的。 “python 表达式单元测试”缺乏响应命中, “python lambda unittest”、“python 表达式模拟”或“python lambda 模拟” 暗示我可能问错了问题。我需要模拟 lambda 表达式的假设是错误的吗?
我知道一个简单的编码解决方案是保留原始代码,但此时我更感兴趣的是填补我的知识空白。
【问题讨论】:
为什么要这样组合这些功能呢?如果您希望能够模拟template_1
的功能,最好让它暴露在模拟中。
@user2357112。 +1 质疑在需要测试时使用 lambda 表达式是否明智,因为它也是 A.P.I. 的一部分。对于代码。然而,我的好奇心是针对一个更简单的问题,即如果情况需要使用它,如何对其进行测试。在我看来,任何有效的 Python 代码都应该是可测试的,程序员需要知道如何测试它。
【参考方案1】:
如果 lambda 表达式可以在某个地方访问,例如类或模块的属性,那么您可以模拟它,但这似乎不太可能。通常,当您不需要对函数的引用时,会使用 lambda 表达式。否则,您只需使用常规函数。
但是,您可以检索模拟对象上所有调用的参数,因此您可以查看传入的 lambda 表达式。在您提供的示例中,最简单的方法就是调用lambda 表达式,看看它返回什么。
from mock import patch
def foo(bar):
return bar()
def baz():
return 42
print foo(baz)
with patch('__main__.foo') as mock_foo:
print foo(baz)
print foo(lambda: 'six by nine')
assert mock_foo.call_args_list[0][0][0]() == 42
assert mock_foo.call_args_list[1][0][0]() == 'six by nine'
如果出于某种原因您不想这样做,那么您可以使用检查模块来查看 lambda 表达式。这是一个仅转储定义函数的源代码行的示例:
from inspect import getsource
from mock import patch
def foo(bar):
return bar()
def baz():
return 42
print foo(baz)
with patch('__main__.foo') as mock_foo:
print foo(baz)
print foo(lambda: 'six by nine')
print mock_foo.call_args_list
for call_args in mock_foo.call_args_list:
print '---'
print getsource(call_args[0][0])
结果:
42
<MagicMock name='foo()' id='140595519812048'>
<MagicMock name='foo()' id='140595519812048'>
[call(<function baz at 0x7fdef208fc08>),
call(<function <lambda> at 0x7fdef208fe60>)]
---
def baz():
return 42
---
print foo(lambda: 'six by nine')
这是您的示例代码通过的测试版本。它测试两种方式:调用模板和检查模板的来源。
# test_lambdademo.py
from inspect import getsource
import unittest
import unittest.mock as mock
import lambdademo
class TestLambda1(unittest.TestCase):
def setUp(self):
p = mock.patch('lambdademo.display_register')
self.mock_display_register = p.start()
self.addCleanup(p.stop)
def test_1(self):
lambdademo.display_pane_1()
self.mock_display_register.assert_called_with(lambdademo.template_1)
def test_2(self):
lambdademo.display_pane_2()
template = self.mock_display_register.call_args[0][0]
template_content = template()
template_source = getsource(template)
self.assertEqual('hello mum', template_content)
self.assertIn('hello mum', template_source)
【讨论】:
您的有趣而深思熟虑的答案将 lambda 表达式从被测代码转移到了测试。看来您的函数foo
充当 lambda 表达式和使用它的代码之间的垫片。如果我将此 shim 模式应用于我的原始函数 display_pane_2
,我最终会得到三个函数:display_pane_2
、shim 和 lambda 表达式。这表明单个函数确实是不可测试的。
我不确定你所说的垫片是什么意思,@lemi57ssss。我已经更新了我的答案,包括对您的原始示例代码的测试。
我在一般意义上使用了“垫片”一词,它指的是它所分离的组件的主要功能不需要的垫片。它在那里支持一些次要功能,在这种情况下,即测试。 'shim(computing)' 的***条目表明,这种一般用途已演变成一个无用的术语。 en.wikipedia.org/wiki/Shim_(computing)你的更新很有启发性。以上是关于是否可以模拟 lambda 表达式?的主要内容,如果未能解决你的问题,请参考以下文章
通过 ElemMatch 查询集合类型的字段是否符合某条件转换成 Lambda表达式的形式