2017-06-19 5 views
1

I持って次の設定クラス:クラスをモックと返すいくつかの値

from config.ConfigB import ConfigB 

class FileRunner(object): 
    def runProcess(self, cfgA) 
    for file in cfgA.listFiles: 
     cfgB = ConfigB(file) 
     print(cfgB.Id) 
     print(cfgB.fileName) 

が、私が作成をテストするには:以下のクラスで複数回インスタンスとプロパティがアクセスされ

class ConfigB(object): 
    Id = None 
    fileName = None 

    def __init__(self, file): 
    self.Id = self.searchForId(file) 
    self.fileName = file 

私がFileRunnerクラスのためにConfigBをモックする次のテストクラス:

import unittest 
import unittest.mock imort MagicMock 
import mock 
from FileRunner import FileRunner 

class TestFileRunner(unittest.TestCase): 
    @mock.patch('FileRunner.ConfigB') 
    def test_methodscalled(self, cfgB): 

    cfgA = Mock() 
    cfgA.listFiles = ['File1','File2'] 

    cfgB().Id.side_effect = [1,2] 
    cfgB().fileName.side_effect = ['File1','File2'] 

    fileRunner = FileRunner() 


    fileRunner.runProcess(cfgA) 

私は 'id'と 'fileName'の両方に対して複数の値を返すようにcfgBのモックを取得しようとしています。 cfgB().fileName = 'File1'を使用すると、cfgBが 'File1'を2回返すモックを得ることができますが、複数の戻り値を繰り返すことができれば好きです。何かできることはありますか?

*編集:私は上記のテストは、特定の値を返すために機能せず、代わりに、私は次の出力を得ることを明確にしたいと思います:

<MagicMock name='cfgB().Id' id='160833320'> 
<MagicMock name='cfgB().fileName' id='160833320'> 
<MagicMock name='cfgB().Id' id='160833320'> 
<MagicMock name='cfgB().fileName' id='160833320'> 
+0

をあなたのコードに問題がいくつかあります。1.第1の方法は、 '' __init__''はなく、 '' __Init__'です'; 2. '' Id''と '' fileName''はクラス変数であるため、[ここ](https://docs.python.org/3.6/tutorial/classes.html#class-and-インスタンス変数)。単純にそれらを削除して、 '' __init__''メソッドの中にassigmentだけ残すことができます。これは**あなたの問題を解決するかもしれません(私はテストしていないし、解決策ではないかもしれません) –

+0

'__init__'と' __Init__'は私の実際のコードではないタイプミスです。実際の '__init__'メソッドは私のコードがはるかに複雑であるため、戻り値を模擬したいので、' __init__'メソッドを機能させたままにしたくありません。 – EliSquared

答えて

1

ここでの問題は、あなたが実際にside_effectを使用していないということですそれが使用される方法です。ドキュメントhereパー

side_effect属性の状態:

モックが呼ばれるたびに呼び出される関数を。 side_effect属性の を参照してください。例外を発生させたり、戻り値を動的に変更する場合に便利です。 この関数は、同じ引数の をモックとして呼び出され、DEFAULTを返さない限り、この 関数の戻り値が戻り値として使用されます。

ここで実現する重要なことは、機能です。ここの期待は実際にはというと呼ばれるものです。あなたは実際に属性をテストしており、属性は関数のように呼び出されていないので、実際にそれらのside_effect呼び出しをどのように使用しているかで実際にテストを正しく構成しているわけではありません。

あなたがテストしようとしていることに基づいて、少し異なるアプローチをとるべきです。コードを見ると、cfgA.listFilesを反復するときにループ内にConfigBオブジェクトが作成されます。したがって、実際にはConfigB(file)に電話をかけて模擬テストをcfgBとしたときの動作をside_effectで制御しようとしていることを示しています。

また、cfgA.listFilesからconfigBまでの繰り返しのファイル名のように見えます。したがって、あなただけのように任意のファイル名のリストとしてlistFilesを設定することができます。あなたがする必要があるすべては、ここで関心の属性にを含むMockオブジェクトを返すために、あなたのcfgBモックのside_effectに設定されてその後

cfgA.listFiles = ['some_file_name_1', 'some_file_name_2'] 

、適切になど、あなたのテストを続行:

これらの変更を実行
cfgB.side_effect = [ 
    Mock(Id="some_id_1", fileName="some_filename_1"), 
    Mock(Id="some_id_2", fileName="some_filename_2") 
] 

、その後、あなたのコードを持っているあなたのprint文から以下の結果が得られます:

some_id_1 
some_filename_1 
some_id_2 
some_filename_2 

このように、テスト用に設定するファイル名を保持するためのイテレートを設定しました。さらに、今度はConfigBのモックでside_effectが適切に使用され、各繰り返しでテストできる属性を保持している適切な模擬設定オブジェクトを返すようになりました。ここで

は、最終的な試験方法は、すべてのように見えるものです一緒に入れ:

class TestFileRunner(unittest.TestCase): 
    @mock.patch('FileRunner.ConfigB') 
    def test_methodscalled(self, cfgB): 

    cfgA = Mock() 
    cfgA.listFiles = ['some_file_name_1', 'some_file_name_2'] 

    cfgB.side_effect = [ 
     Mock(Id="some_id_1", fileName="some_filename_1"), 
     Mock(Id="some_id_2", fileName="some_filename_2") 
    ] 

    fileRunner = FileRunner() 
    fileRunner.runProcess(cfgA) 
+0

これは私がやりたいことのほとんどを行いますが、これは私の単体テストで予期しない問題を引き起こしているため、最後の質問が1つあります。私は、ファイルランナークラス 'foo(cfgB)'の中に正しいモックで呼び出されたことを確認したいという関数を持っています。私が 'foo.assert_called_with(cfgB())'を実行すると、 'Mock(Id =" some_id_3 "、fileName =" some_filename_3 ")'のside_effect iterableに3行目を追加しない限り、反復可能なエラーが発生します。 assock_called_with文で模擬IDが異なるため、これはエラーをスローします。彼らが呼ばれたことを確認するために、最初の2人のモックを参照するためにとにかくありますか? – EliSquared

関連する問題