2016-11-17 2 views
1

このアプリケーションはkivyさんが作成したものです。 pytestを使って関数をテストしたいのですが、その関数をテストするためにはオブジェクトを最初に初期化する必要がありますが、初期化時にUIのオブジェクトが必要ですが、テスト段階です。 UIから何かを取得する。kivyが作成したアプリケーションをテストするときにUIとやりとりする方法は?

これは、エラーがあり、これは前者のクラスによって使用されるServerMsgオブジェクトを生成するヘルパークラスである

class SaltConfig(GridLayout): 
    def check_phone_number_on_first_contact(self, button): 
     s = self.instanciate_ServerMsg(tt) 

     try: 
      s.send() 
     except HTTPError as err: 
      print("[HTTPError] : " + str(err.code)) 
      return 

     # some code when running without error 

    def instanciate_ServerMsg(): 
     return ServerMsg() 

を処理されたクラスです。

class ServerMsg(OrderedDict): 
    def send(self,answerCallback=None): 
     #send something to server via urllib.urlopen 

これは私のテストコードです:それはUIからいくつかの値を必要とするためinitialzingとき

class TestSaltConfig: 
    def test_check_phone_number_on_first_contact(self): 
     myError = HTTPError(url="http://127.0.0.1", code=500, 
         msg="HTTP Error Occurs", hdrs="donotknow", fp=None) 

    mockServerMsg = mock.Mock(spec=ServerMsg) 
    mockServerMsg.send.side_effect = myError 

    sc = SaltConfig(ds_config_file_missing.data_store) 

    def mockreturn(): 
     return mockServerMsg 

    monkeypatch.setattr(sc, 'instanciate_ServerMsg', mockreturn) 
    sc.check_phone_number_on_first_contact() 

私はオブジェクトを初期化することはできません、それははAttributeErrorがスローされます。

だから私は立ち往生します。

オブジェクトをモックして元の関数にパッチを適用しようとしましたが、関数自体にUIに関連するロジックがあるため機能しません。

どうすれば解決できますか?ありがとう

+1

デザイン上の瑕疵のようです。ロジックはUIに依存すべきではありません。その理由の1つは、それを孤立してテストできることです。 –

答えて

1

私は簡単なランナー - KivyUnitTestと一緒にKivyアプリをテストするための記事を作った。 pytestではなくunittestで動作しますが、必要に応じて書き換えが難しいはずはありません。

button = <button you found in widget tree> 
button.dispatch('on_release') 

、より多く:記事では、UIのメインループと、あなたは喜んでこれを行って、ボタンで行うことができますこの方法を「浸透」する方法を説明します。基本的には、このようなテストで何かを行うことができ、それぞれの機能を個別にテストする必要はありません。つまり、良い習慣ですが、(主にUIをテストする場合は)主に50種類のテストに入り込むことはできません。

この方法では、アプリを使用する際にカジュアルなユーザーと同じことを行うことができます。したがって、カジュアルな方法でテストするときに問題が発生する可能性もあります。いくつかの奇妙な/予期しないユーザーの動作。

はここでスケルトンの:しかし

import unittest 

import os 
import sys 
import time 
import os.path as op 
from functools import partial 
from kivy.clock import Clock 

# when you have a test in <root>/tests/test.py 
main_path = op.dirname(op.dirname(op.abspath(__file__))) 
sys.path.append(main_path) 

from main import My 


class Test(unittest.TestCase): 
    def pause(*args): 
     time.sleep(0.000001) 

    # main test function 
    def run_test(self, app, *args): 
     Clock.schedule_interval(self.pause, 0.000001) 

     # Do something 

     # Comment out if you are editing the test, it'll leave the 
     # Window opened. 
     app.stop() 

    def test_example(self): 
     app = My() 
     p = partial(self.run_test, app) 
     Clock.schedule_once(p, 0.000001) 
     app.run() 

if __name__ == '__main__': 
    unittest.main() 

、トマスが言ったようにそれを行うための効率的なことだとき、あなたは、言ったときに可能なUIとロジックを分離、またはより良いはずです。 UIとの通信を必要とする単一の関数をテストするためだけに大きなアプリケーション全体を模擬したくはありません。

0

最終的には完成したばかりですが、もっと洗練されたソリューションが必要だと思います。すべての行がs.send()ステートメント以外の単なる値割り当てであるという事実を考えれば、その考えは簡単です。

次に、テストフェーズで何らかのエラーがポップアップするたびに(オブジェクトにUIの値が不足している)、元のオブジェクトをモックするだけで、テストメソッドが最終的にテストできるまでこのステップを繰り返します関数がHTTPErrorを処理できるかどうか。

この例では、幸運なクラスPhoneNumberをモックするだけですが、何度か処理しなければならない場合があるので、明らかに@KeyWeeUsrの答えは本番環境にとって理想的です。しかし、私はちょうどすぐに解決を望む誰かのためにここに私の考えを記載します。

@pytest.fixture 
def myHTTPError(request): 
    """ 
    Generating HTTPError with the pass-in parameters 
    from pytest_generate_tests(metafunc) 
    """ 
    httpError = HTTPError(url="http://127.0.0.1", code=request.param, 
          msg="HTTP Error Occurs", hdrs="donotknow", fp=None) 
    return httpError 

class TestSaltConfig: 
    def test_check_phone_number(self, myHTTPError, ds_config_file_missing): 
     """ 
     Raise an HTTP 500 error, and invoke the original function with this error. 
     Test to see if it could pass, if it can't handle, the test will fail. 
     The function locates in configs.py, line 211 
     This test will run 2 times with different HTTP status code, 404 and 500 
     """ 

     # A setup class used to cover the runtime error 
     # since Mock object can't fake properties which create via __init__() 
     class PhoneNumber: 
      text = "610274598038" 

     # Mock the ServerMsg class, and apply the custom 
     # HTTPError to the send() method 
     mockServerMsg = mock.Mock(spec=ServerMsg) 
     mockServerMsg.send.side_effect = myHTTPError 

     # Mock the SaltConfig class and change some of its 
     # members to our custom one 
     mockSalt = mock.Mock(spec=SaltConfig) 
     mockSalt.phoneNumber = PhoneNumber() 
     mockSalt.instanciate_ServerMsg.return_value = mockServerMsg 
     mockSalt.dataStore = ds_config_file_missing.data_store 

     # Make the check_phone_number_on_first_contact() 
     # to refer the original function 
     mockSalt.check_phone_number = SaltConfig.check_phone_number 

     # Call the function to do the test 
     mockSalt.check_phone_number_on_first_contact(mockSalt, "button") 
関連する問題