2012-04-24 7 views
5

Ploneアドオンに対する単純な機能テストを作成するためにSeleniumを使用したいと思います。主なドライバは、Webブラウザで何が起きているかを見るため、非プログラマがテストケースを作成して理解できるようにすることです。記録する機能性Ploneテストケースのセレンテスト

  • テストケースは、テストが実行されますPloneサイトの環境を準備するに推奨されるベストプラクティスは何ですか

  • どのように実行するために(アドオンをインストールし、メールホストをあざけりは、サンプルコンテンツを作成します)ブラウザーでSelenium録音を開始できる点と、録音を有効にしてブラウザーを開く方法については、Ploneの機能テストケースを参照してください。

  • 後でPythonコードから記録されたテスト出力を実行しますか?

Ploneと組み合わせる他のテスト記録フレームワークはありますか? JavascriptのUIに対するテストが可能であることが条件です。

答えて

0

plone.app.testingには4.1以降のセレニウムテスト層が付属しています。ここで

は自分より洗練されたヘルパーコードです:

""」(まだgithubの上に存在しない)

Some PSE 2012 Selenium notes 

    * https://github.com/plone/plone.seleniumtesting 

    * https://github.com/emanlove/pse2012 

    Selenium WebDriver API 

    * http://code.google.com/p/selenium/source/browse/trunk/py/selenium/webdriver/remote/webdriver.py 

    Selenium element match options 

    * http://code.google.com/p/selenium/source/browse/trunk/py/selenium/webdriver/common/by.py 

    Selenium element API (after find_xxx()) 

    * http://code.google.com/p/selenium/source/browse/trunk/py/selenium/webdriver/remote/webelement.py 

    You can do pdb debugging using ``selenium_helper.selenium_error_trapper()`` if you run 
    tests with ``SELENIUM_DEBUG`` turned on:: 

     SELENIUM_DEBUG=true bin/test -s testspackage -t test_usermenu 

    Then you'll get debug prompt on any Selenium error. 


""" 

import os 

# Try use ipdb debugger if we have one 
try: 
    import ipdb as pdb 
except ImportError: 
    import pdb 

from selenium.webdriver.common.by import By 
from selenium.common.exceptions import NoSuchElementException 
from selenium.common.exceptions import WebDriverException 
from selenium.webdriver.support.wait import WebDriverWait 

from plone.app.testing import selenium_layers 

SELENIUM_DEBUG = "SELENIUM_DEBUG" in os.environ 


class SeleniumTrapper(object): 
    """ 
    With statement for break on Selenium errors to ipdb if it has been enabled for this test run. 
    """ 

    def __init__(self, driver): 
     self.driver = driver 

    def __enter__(self): 
     pass 

    def __exit__(self, type, value, traceback): 
     """ 
     http://effbot.org/zone/python-with-statement.htm 
     """ 
     if isinstance(value, WebDriverException) and SELENIUM_DEBUG: 
      # This was Selenium exception 
      print "Selenium API call failed because of browser state error: %s" % value 
      print "Selenium instance has been bound to self.driver" 
      pdb.set_trace() 


class SeleniumHelper(object): 
    """ 
    Selenium convenience methods for Plone. 

    Command Selenium browser to do common actions. 
    This mainly curries and delegates to plone.app.testing.selenium_layers helper methods. 

    More info: 

    * https://github.com/plone/plone.app.testing/blob/master/plone/app/testing/selenium_layers.py 
    """ 

    def __init__(self, testcase, driver=None): 
     """ 
     :param testcase: Yout test class instance 

     :param login_ok_method: Selenium check function to run to see if login success login_ok_method(selenium_helper) 
     """ 
     self.testcase = testcase 
     if driver: 
      # Use specific Selenium WebDriver instance 
      self.driver = driver 
     else: 
      # plone.app.tesrting selenium layer 
      self.driver = testcase.layer['selenium'] 
     self.portal = testcase.layer["portal"] 

    def selenium_error_trapper(self): 
     """ 
     Create ``with`` statement context helper which will invoke Python ipdb debugger if Selenium fails to do some action. 

     If you run test with SELENIUM_DEBUG env var set you'll get dropped into a debugger on error. 
     """ 
     return SeleniumTrapper(self.driver) 

    def reset(self): 
     """ 
     Reset Selenium test browser between tests. 
     """ 

    def login(self, username, password, timeout=15, poll=0.5, login_cookie_name="__ac", login_url=None): 
     """ 
     Perform Plone login using Selenium test browser and Plone's /login_form page. 
     """ 

     submit_button_css = '#login_form input[name=submit]' 

     if not login_url: 
      # Default Plone login URL 
      login_url = self.portal.absolute_url() + '/login_form' 

     with self.selenium_error_trapper(): 
      submit_button = self.open(login_url, wait_until_visible=submit_button_css) 

      self.find_element(By.CSS_SELECTOR, 'input#__ac_name').send_keys(username) 
      self.find_element(By.CSS_SELECTOR, 'input#__ac_password').send_keys(password) 

      submit_button.click() 

     # Check that we get Plone login cookie before the timeout 
     waitress = WebDriverWait(self.driver, timeout, poll) 
     matcher = lambda driver: driver.get_cookie(login_cookie_name) not in ["", None] 
     waitress.until(matcher, "After login did not get login cookie named %s" % login_cookie_name) 

    def logout(self, logout_url=None): 
     """ 
     Perform logout using Selenium test browser. 

     :param logout_url: For non-default Plone logout view 
     """ 

     if not logout_url: 
      logout_url = self.portal.absolute_url() + "/logout" 

     self.open(logout_url) 

    def get_plone_page_heading(self): 
     """ 
     Get Plone main <h1> contents as lowercase. 

     XXX: Looks like Selenium API returns uppercase if there is text-transform: uppercase? 

     :return: Empty string if there is no title on the page (convenience for string matching) 
     """ 

     try: 
      title_elem = self.driver.find_element_by_class_name("documentFirstHeading") 
     except NoSuchElementException: 
      return "" 

     if not title_elem: 
      return "" 

     return title_elem.text.lower() 

    def trap_error_log(self, orignal_page=None): 
     """ 
     Read error from the site error log and dump it to output. 

     Makes debugging Selenium tests much more fun when you directly see 
     the actual errors instead of OHO. 

     :param orignal_page: Decorate the traceback with URL we tried to access. 
     """ 

     # http://svn.zope.org/Zope/trunk/src/Products/SiteErrorLog/SiteErrorLog.py?rev=96315&view=auto 
     error_log = self.portal.error_log 
     entries = error_log.getLogEntries() 

     if len(entries) == 0: 
      # No errors, yay! 
      return 

     msg = "" 

     if orignal_page: 
      msg += "Plone logged an error when accessing page %s\n" % orignal_page 

     # We can only fail on traceback 
     if len(entries) >= 2: 
      msg += "Several exceptions were logged.\n" 

     entry = entries[0] 

     raise AssertionError(msg + entry["tb_text"]) 

    def is_error_page(self): 
     """ 
     Check that if the current page is Plone error page. 
     """ 
     return "but there seems to be an error" in self.get_plone_page_heading() 

    def is_unauthorized_page(self): 
     """ 
     Check that the page is not unauthorized page. 

     ..note :: 

      We cannot distingush login from unauthorized 
     """ 

     # require_login <-- auth redirect final target 
     return "/require_login/" in self.driver.current_url 

    def is_not_found_page(self): 
     """ 
     Check if we got 404 
     """ 
     return "this page does not seem to exist" in self.get_plone_page_heading() 

    def find_element(self, by, target): 
     """ 
     Call Selenium find_element() API and break on not found and such errors if running tests in SELENIUM_DEBUG mode. 
     """ 
     with self.selenium_error_trapper(): 
      return self.driver.find_element(by, target) 

    def find_elements(self, by, target): 
     """ 
     Call Selenium find_elements() API and break on not found and such errors if running tests in SELENIUM_DEBUG mode. 
     """ 
     with self.selenium_error_trapper(): 
      return self.driver.find_elements(by, target) 

    def click(self, by, target): 
     """ 
     Click an element. 

     :param by: selenium.webdriver.common.by.By contstant 

     :param target: CSS selector or such 
     """ 
     with self.selenium_error_trapper(): 
      elem = self.driver.find_element(by, target) 
      elem.click() 

    def open(self, url, check_error_log=True, check_sorry_error=True, check_unauthorized=True, check_not_found=True, wait_until_visible=None): 
     """ 
     Open an URL in Selenium browser. 

     If url does not start with http:// assume it is a site root relative URL. 

     :param wait_until_visible: CSS selector which must match before we proceed 

     :param check_error_log: If the page has created anything in Plone error log then dump this traceback out. 

     :param check_sorry_error: Assert on Plone error response page 

     :param check_unauthorized: Assert on Plone Unauthorized page (login dialog) 

     :return: Element queried by wait_until_visible or None 
     """ 
     elem = None 

     # Convert to abs URL 
     if not (url.startswith("http://") or url.startswith("https://")): 
      url = self.portal.absolute_url() + url 

     selenium_layers.open(self.driver, url) 

     if check_error_log: 
      self.trap_error_log(url) 

     if wait_until_visible: 
      elem = self.wait_until_visible(By.CSS_SELECTOR, wait_until_visible) 

     # XXX: These should be waited also 

     if check_sorry_error: 
      self.testcase.assertFalse(self.is_error_page(), "Got Plone error page for url: %s" % url) 

     if check_unauthorized: 
      self.testcase.assertFalse(self.is_unauthorized_page(), "Got Plone Unauthorized page for url: %s" % url) 

     if check_not_found: 
      self.testcase.assertFalse(self.is_not_found_page(), "Got Plone not found page for url: %s" % url) 

     return elem 

    def wait_until_visible(self, by, target, message=None, timeout=10, poll=0.5): 
     """ 
     Wait until some element is visible on the page (assume DOM is ready by then). 

     Wraps selenium.webdriver.support.wait() API. 

     http://selenium.googlecode.com/svn/trunk/docs/api/py/webdriver_support/selenium.webdriver.support.wait.html#module-selenium.webdriver.support.wait 
     """ 

     if not message: 
      message = "Waiting for element: %s" % target 

     waitress = WebDriverWait(self.driver, timeout, poll) 
     matcher = lambda driver: driver.find_element(by, target) 
     waitress.until(matcher, message) 
     elem = self.driver.find_element(by, target) 
     return elem 

1

私の推測では、個々のステップを個別に実行するツールがありますが、それらのツールは、記述したとおりに正しく動作しません。

私の経験では、記録されたテストの品質は非常に悪く、プログラマはとにかく書き直す必要があります。あなたがJavaScriptをたくさん持っているときだけ悪化します。さらに、AJAXを使用するサイトがある場合、発生する問題の1つは、次のクリックを行う前に特定の要素が表示されるのを待たなければならない場合があり、これがほとんどのレコーダーが失敗する場所です。

私はまた、エンドユーザーをターゲットとしたツールについて聞いてみたいと思うし、Ploneテストを自分で記録して実行することができます。もし誰かがこの種のプロジェクトについて知っていたら、その開発に。

+0

[OK]を私は恐れていたので、どのようにプログラム的にセレンを運転についてのあなた。 (pdb設定で)ブラウザのすべてのステップをプレビューするのですか? –

+0

PythonバインディングをSeleniumに使用することは非常に簡単です:http://pypi.python.org/pypi/selenium –

+0

私はバインディングをよく知っています。質問の文脈で希望がある箇条書きで示されている。 –