2013-06-03 10 views
9

私はグリップを得ることができないような厄介な問題があります。私は 現在、djangoのカスタムauth-backendの単体テストを書いています。私たちの システムでは、実際には2つのバックエンドがあります.1つは組み込みdjangoバックエンド と、XML形式のユーザー情報を返すJavaベースのAPI にリクエストを送信するカスタムバックエンドです。今、私はユニット のテストを書いているので、私は 私はJava APIをテストしようとしていないので、 のようなシステムの外でリクエストを送信したくないので、私はどうすればこのことを回避できますか?副作用が最も堅牢な方法で発生します。Django UnittestsのリモートサーバとAPIをきちんと模倣してください

私がテストしてい関数は、URL 設定値が は、ユーザー名とパスワードのデータを認証し、XMLを返すJavaサーバーのためだけにベースURLで、サービスの値が ほんの一部であるこのようなものであり、 のために、私たちにその重要でないが、URLのクエリを構築するための魔法:だから

@staticmethod 
def get_info_from_api_with_un_pw(username, password, service=12345): 
    url = settings.AUTHENTICATE_URL_VIA_PASSWORD 
    if AUTH_FIELD == "username": 
     params = {"nick": username, "password": password} 
    elif AUTH_FIELD == "email": 
     params = {"email": username, "password": password} 
    params["service"] = service 
    encoded_params = urlencode([(k, smart_str(v, "latin1")) for k, v in params.items()]) 
    try: 
     # get the user's data from the api 
     xml = urlopen(url + encoded_params).read() 
     userinfo = dict((e.tag, smart_unicode(e.text, strings_only=True)) 
         for e in ET.fromstring(xml).getchildren()) 
     if "nil" in userinfo: 
      return userinfo 
     else: 
      return None 

、我々はXMLを取得し、辞書にそれを解析し、キーはnilが存在する場合 はその後、私たちは辞書を返すと幸せに運ぶことができます認証された。 は明らかに、一つの解決策だけで何とかXML変数にモンキーパッチのロジックをオーバーライドしたり、 する方法を見つけることですが、私はこの答えを見つけました:

How can one mock/stub python module like urllib

私はそのような何かを実装しようとしたが、詳細が 非常にスケッチであり、私はそれを得ることができなかった。

私はまた、XMLレスポンスを捕獲し、テスト機能のurlパラメータに渡され、モック 応答としてそれを使用する方法を見つけることを意図して テストフォルダ内のローカルファイルに入れて、 このようなものは、URLを上書きします:

@override_settings(AUTHENTICATE_URL_VIA_PASSWORD=(os.path.join(os.path.dirname(__file__), "{0}".format("response.xml")))) 
def test_get_user_info_username(self): 
    self.backend = RemoteAuthBackend() 
    self.backend.get_info_from_api_with_un_pw("user", "pass") 

しかし、それはまた、(すなわち、「URL + encoded_pa​​rams」)、 関数が定義され、そのURLの建物ロジックを考慮する必要があります。繰り返しますが、 という名前のレスポンスファイルを連結URLと同じ名前に変更することもできますが、これは となり、機能のための単体テストと「カンニング」のようにはなりません。全体が というものがますます増えていますとにかく、これらの解決策とその本当にちょうど備品で脆い常に、私は避けたいものでもある場合は すべて可能です。

また、django開発サーバーでxmlを提供する方法があるのか​​もしれないと思っていました。それは衛生的な解決策のようだが、多くのグーグル・グーグルは、そのようなことが可能であるかまたは推奨される場合には私に手がかりを与えなかったし、開発環境の外で実行するテストではないと思う。

だから、理想的に、私は何とか に「サーバー」をモック関数呼び出しでJava APIの場所を取る、または何らかの形で機能がそのURLとして開くことができ、いくつかのXMLペイロードを を果たすことができるようにする必要があります、 monkeypatchテスト自体の機能、または...

モックライブラリには、このようなことを行うのに適したツールがありますか?

http://www.voidspace.org.uk/python/mock

だから、この質問1には2つの点)がある私は、きれいな方法で私の 特定の問題を解決したい、そしてもっと重要なのは2でしょう) きれいにDjangoのユニットを記述するためのベストプラクティス何ですかあなたのドメイン外のリモート APIからのユーザー認証のために、データ、クッキーなどに依存している場合は、 をテストしますか?

答えて

1

モックライブラリは正しく使用するとうまくいくはずです。私はminimockライブラリを好んでおり、これに役立つ小さなベースユニットのテストケース(minimocktest)を書きました。

あなたは次のようにあなたがそれを行うことができますurllibテストするためにはDjangoでこのテストケースを統合する場合:

from minimocktest import MockTestCase 
from django.test import TestCase 
from django.test.client import Client 

class DjangoTestCase(TestCase, MockTestCase): 
    ''' 
    A TestCase class that combines minimocktest and django.test.TestCase 
    ''' 

    def _pre_setup(self): 
     MockTestCase.setUp(self) 
     TestCase._pre_setup(self) 
     # optional: shortcut client handle for quick testing 
     self.client = Client() 

    def _post_teardown(self): 
     TestCase._post_teardown(self) 
     MockTestCase.tearDown(self) 

今、あなたが代わりに直接Djangoのテストケースを使用してのこのテストケースを使用することができます。

class MySimpleTestCase(DjangoTestCase): 
    def setUp(self): 
     self.file = StringIO.StringIO('MiniMockTest') 
     self.file.close = self.Mock('file_close_function') 
    def test_urldump_dumpsContentProperly(self): 
     self.mock('urllib2.urlopen', returns=self.file) 
     self.assertEquals(urldump('http://pykler.github.com'), 'MiniMockTest') 
     self.assertSameTrace('\n'.join([ 
      "Called urllib2.urlopen('http://pykler.github.com')", 
      "Called file_close_function()", 
     ])) 
     urllib2.urlopen('anything') 
     self.mock('urllib2.urlopen', returns=self.file, tracker=None) 
     urllib2.urlopen('this is not tracked') 
     self.assertTrace("Called urllib2.urlopen('anything')") 
     self.assertTrace("Called urllib2.urlopen('this is mocked but not tracked')", includes=False) 
     self.assertSameTrace('\n'.join([ 
      "Called urllib2.urlopen('http://pykler.github.com')", 
      "Called file_close_function()", 
      "Called urllib2.urlopen('anything')", 
     ])) 
+0

あなたの場合、次のものをモックすることをお勧めします。あなたのモデルファイル 'from urllib import urlopen'に以下のようにインポートしたようですので、self.mock( 'models.urlopen')' – Pykler

+1

ああ、それはすばらしいことです。そのような模倣がちょうどチケットであるように見えます。どうもありがとう! – osman

0

ここでは、私が記録に残した解決策の基本について説明します。私は最終的にMockitoではなくMockライブラリを使用しましたが、そのアイデアは同じです:

from mock import patch 

@override_settings(AUTHENTICATE_LOGIN_FIELD="username") 
@patch("mymodule.auth_backend.urlopen") 
def test_get_user_info_username(self, urlopen_override): 
    response = "file://" + os.path.join(os.path.dirname(__file__), "{0}".format("response.xml")) 
    # mock patch replaces API call 
    urlopen_override.return_value = urlopen(response) 
    # call the patched object 
    userinfo = RemoteAuthBackend.get_info_from_api_with_un_pw("user", "pass") 
    assert_equal(type(userinfo), dict) 
    assert_equal(userinfo["nick"], "user") 
    assert_equal(userinfo["pass"], "pass") 
関連する問題