2016-12-04 16 views
2

最初の2つの機能display_pane_1template_1は、test_1の方法で簡単に でテストされます。私はこれらの2つの機能を単一の機能にリファクタリングしたいです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 x: 'hell mum'が失敗するかどうかをテストしたいと思います。

ソリューションへの2つのパスを試しました。

最初のオプションは、の単純なコピーで、lambdademo.template_1の引数をlambdaのモックに置き換えます。私はラムダのような表現をどのように模倣すべきかを示唆しているマニュアルの中には何も見つかりませんでした。 マニュアルに記載されている場合は、どこに教えてください。

私の2番目の選択肢は、スタックオーバーフローとインターネット上での幅広い検索の結果です。 'python expression unittest'、 'python lambda unittest'、 'python expression mock'、または 'python lambda mock' が、間違った質問をしている可能性があると回答しました。間違ったラムダ式を模倣する必要があるという私の前提ですか?

私は、単純なコード化の解決策は元のコードを保持することであると認識していますが、この時点で私の知識のギャップを埋めることにもっと興味があります。

+1

これらの機能をこのように組み合わせる理由は何ですか? 'template_1'の機能をモックできるようにしたいのであれば、それを黙らせておくことをお勧めします。 – user2357112

+0

@ user2357112それがA.P.I.の一部でもあるため、ラムダ式をテストする必要があるときにラムダ式を使用することの知恵に疑問を呈するために+1。コードのために。しかし、私の好奇心は、それが使用されることが状況によっては、それをテストするという簡単な質問に向けられていました。どのような有効なPythonコードでもテスト可能であり、プログラマーはそれをテストする方法を知る必要があると私には思われます。 – lemi57ssss

答えて

2

クラスやモジュールの属性のようにラムダ式にアクセスできる場合は、モックできますが、これはほとんど起こりそうにないようです。通常、ラムダ式は、関数への参照を必要としないときに使用されます。それ以外の場合は、通常の関数を使用するだけです。

しかし、モックオブジェクトのすべての呼び出しの引数を取得することができるので、渡されたラムダ式を見ることができます。与えたもののような例では、最も単純なことはラムダ式を呼び出し、それが何を返すかを見てください。

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モジュールを使用してラムダ式を調べることができます。ここだけの機能が定義されたソースコード行をダンプする例を示します

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

ラムダ式をテスト中のコードからテストに移動すると面白い答えがあります。あなたの関数 'foo'はラムダ式とそれが使われているコードとの間のシムとして働いているようです。このshimパターンを私の元の関数 'display_pane_2'に適用すると、' display_pane_2'、shim、およびlambda式の3つの関数で終わります。これは、単一の機能が確かにテスト可能でないことを示唆している。 – lemi57ssss

+1

シムの意味がわかりません、@ lemi57ssss。元のサンプルコードのテストを含めるように答えを更新しました。 –

+0

私はスペーサーの一般的な意味で「シム」という用語を使用しました。スペーサーは、それが分離する成分の主な機能には必要ではありません。この場合、テストのいくつかの二次機能をサポートすることがあります。 'シム(計算)'のためのウィキペディアのエントリーは、この一般的な使用が役に立たない用語に変わったことを示唆している。 https://en.wikipedia.org/wiki/Shim_(computing) 更新が最も明るかったです。 – lemi57ssss

関連する問題