GAE DjangoのSeleniumテストをどのように拷問したか

先史時代



GAE Djangoで書かれたプロジェクトに参加したら、Seleniumでテストを実装する必要がありました。 残念ながら、このための既製のツールは見つかりませんでした。 インターネットでの検索では肯定的な結果は得られませんでした。



間違った方向を向ける



最初に思いついたのは、setUpでサーバーを起動することでした
subprocess.Popen
      
      



分解メソッドで完了します
 self.server_process.terminate()
      
      





一見-実用的なソリューション。 発生した最初の質問:

実行中のサーバーインスタンスとテストベッドで同じデータベースとサービスを使用するように強制するにはどうすればよいですか?



サーバーを起動するときに、使用するデータベースを指定します。



 --use_sqlite --datastore_path=/full/path/to/sqlite_db
      
      





testbedに、どのapp_idおよびどのベースからテストサーバーを起動するかを指示します。



 self.testbed.setup_env(app_id='some-app-id') self.testbed.init_datastore_v3_stub(use_sqlite=True, datastore_file='/full/path/to/sqlite_db')
      
      





準備はできているように見えますが、いわばテストベッドとサーバーの間の競合が原因で問題が発生しています。 サーバーは必要なサービスを開始し、テストベッドも同じことを行います。 一般的に、「最後のパパは誰ですか」。 通常の作業ベースを取得することはできませんでした。 その結果、初期化順序を選択した後、私は成功し、最初のテストは成功しました。 勝利は近いように見えたが、考えが私を離れることは決してなかった。これらのテストをさらに進めるほど、「汚い」決定の深ofに深く入り込む。



ベースをきれいにする必要があります



testbed.deactivate()およびtest_server.terminate()と呼ばれるteardown()メソッド。

ベースはクリアされませんでした。 サーバーの起動コマンドに--clear_datastoreを追加すると、初期化中にすべてが壊れました。 データベースからすべてのデータを削除し、キャッシュを手動でクリアすると、競合も発生しました。



 db.delete(db.Query()) memcache.flush_all()
      
      





os.removeデータベースファイルの削除を追加する必要がありましたが、これはすべての問題を解決しませんでした。



1つの問題を解決するたびに、別の問題が発生しました。 私が書いたものから、手が落ち、悪化した。 寝ました。



独創的なものはすべてシンプルです



それほど単純ではありませんが、上記の説明よりも単純です。 LiveServerTestCaseを使用するというアイデアを思いついた同僚に感謝します。 それは救いになりました。



テストベッド全体をアクティブにし、テストケース全体の終了時に非アクティブにする基本クラス:



 class GAELiveServerTestCase(LiveServerTestCase): @classmethod def setUpClass(cls): cls.testbed = testbed.Testbed() cls.testbed.activate() cls.testbed.init_datastore_v3_stub() cls.testbed.init_memcache_stub() cls.testbed.init_channel_stub() cls.testbed.init_urlfetch_stub() cls.testbed.init_user_stub() super(GAELiveServerTestCase, cls).setUpClass() @classmethod def tearDownClass(cls): cls.testbed.deactivate() super(GAELiveServerTestCase, cls).tearDownClass()
      
      







実際にクラスのセレンテスト。

 class SeleniumTestCase(GAELiveServerTestCase): def setUp(self): self.driver = webdriver.Chrome() def tearDown(self): self.driver.close() self.driver.quit() db.delete(db.Query()) memcache.flush_all()
      
      







そして1つの重要なポイント。



LiveServerTestCaseは、タイプ_ahのGAEインスタンスを開始しません。 チャンネルが必要でした。 1つのプロジェクトを見て、gae-shyソートを掘り、追加のビューが描画されました。たとえば、jsapiのgaeからコピーされたロジックと、接続、切断、ポーリング信号があります。



 from google.appengine.tools.devappserver2.channel import _JSAPI_PATH def channel_stub_view(request, page): params = request.REQUEST if page == 'jsapi': return HttpResponse(content=open(_JSAPI_PATH).read(), content_type='text/javascript') elif page == 'dev': command = params.get('command', None) token = params.get('channel', None) if command is None or token is None: return HttpResponse(status=400) stub = apiproxy_stub_map.apiproxy.GetStub('channel') try: stub.connect_channel(token) except (channel_service_stub.InvalidTokenError, channel_service_stub.TokenTimedOutError): return HttpResponse(status=401) client_id = stub.validate_token_and_extract_client_id(token) if command == 'connect': return HttpResponse(content='1', content_type='text/plain') elif command == 'poll': message = stub.pop_first_message(token) if message is not None: return HttpResponse(content=message, content_type='application/json') return HttpResponse()
      
      







テスト中にのみ利用可能になりました:



 if settings.TESTING: urlpatterns += patterns('', url(r'^_ah/channel/(?P<page>.*)$', 'channel_stub_view'), )
      
      







切断が発生するとき(たとえば、ページが閉じられたとき)に問題が1つだけあり、テストベッドはこれを理解しませんでした。 ページを開くたびにchannel_stubを初期化することで問題を解決しました。



 class SeleniumTestCase(GAELiveServerTestCase): def setUp(self): self.driver = webdriver.Chrome() def tearDown(self): self.driver.quit() db.delete(db.Query()) memcache.flush_all() def open_url(self, url): self.get(urljoin(self.live_server_url, url)) self.testbed.init_channel_stub()
      
      







PS私はこのソリューションが誰かを助けることを期待して投稿を書きました、そして彼は同じような仕事に多くの時間と神経を費やさないでしょう。



All Articles