2016-12-04 38 views
2

前兩個函數display_pane_1template_1在方法test_1中易於測試 。我想將這兩個函數重構爲一個函數display_pane_2是否可以模擬一個lambda表達式?

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的簡單副本,用lambda的模擬替換參數lambdademo.template_1。我在手冊中找不到任何內容,暗示我應該如何嘲笑像lambda這樣的表達式。 如果是在手冊中,請告訴我在哪裏。

我的第二個選擇是在Stack Overflow和Internet上進行更廣泛的搜索。 'python expression unittest'缺少響應命中, 'python lambda unittest','python expression mock'或'python lambda mock' 暗示我可能會問錯誤的問題。我的假設是否需要模擬lambda表達式?

我知道一個簡單的編碼解決方案將保留原始代碼,但在這一點上我更感興趣填補我的知識空白。

+1

爲什麼要以這種方式組合這些功能?如果你想能夠模擬'template_1'的功能,似乎最好將它暴露給嘲笑。 – user2357112

+0

@ user2357112。 +1,因爲它也是A.P.I.的一部分,所以在需要測試時使用lambda表達式的智慧進行質疑。代碼。但是,我的好奇心是針對如果情況要求使用它的情況下如何測試它的一個簡單問題。在我看來,任何有效的Python代碼都應該是可測試的,程序員需要知道如何測試它。 – lemi57ssss

回答

2

如果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' 

如果由於某種原因您不想這樣做,那麼您可以使用inspect模塊來查看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) 
+0

您的有趣和深思熟慮的答案將lambda表達式從測試中的代碼移動到測試。看起來你的函數'foo'充當lambda表達式和使用它的代碼之間的墊片。如果我將這個填充圖案應用於我的原始函數'display_pane_2',我最終會得到三個函數:'display_pane_2',墊片和lambda表達式。它表明單一功能確實是不可測試的。 – lemi57ssss

+1

我不確定你的意思是什麼,@ lemi57ssss。我已經更新了我的答案,包括對原始示例代碼的測試。 –

+0

我在其一般意義上使用了「墊片」這一術語,它不是分離組件主要功能所需的墊片。在這裏支持一些輔助功能,在這種情況下是測試。 'shim(計算)'的維基百科條目表明,這種一般用途已演變成一個無用的術語。 https://en.wikipedia.org/wiki/Shim_(computing) 您的更新最具啓發性。 – lemi57ssss

相關問題