2012-04-24 110 views
5

我想用Selenium创建针对Plone附件的简单功能测试。这里的主要驱动力是非编程人员可以通过一些努力来创建和理解测试用例,因为他们在Web浏览器中看到发生了什么。什么是记录功能性Plone测试用例的硒测试

  • 测试用例准备在测试将运行Plone站点环境的推荐最佳实践(安装插件,嘲笑了邮件主机,创建样品含量)

  • 如何运行Plone功能测试用例,您可以在浏览器中启动Selenium录制,以及如何在启用录制的情况下打开浏览器?

  • 以后如何从Python代码运行记录的测试输出?

是否还有其他测试记录框架与Plone结合?能够测试与Javacripted UI是一个要求。

回答

0

plone.app.testing自4.1开始提供了seleniumtestlayer。

这是我自己的更复杂的助手代码:

“””

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 

(不GitHub上还)

1

我的猜测是有些工具可以独立完成各个步骤,但这些工具并不像您所描述的那样一起工作。

根据我的经验,记录测试的质量非常糟糕,程序员无论如何都不得不重写它们。当你有很多JavaScript的时候它会变得更糟。另外,如果您有一个使用AJAX的网站,则发生的一个问题是您有时必须等待某个特定元素出现,然后才能进行下一次点击,并且这是大多数录像机出现故障的地方。

我也很想听听一个针对最终用户的工具,并允许他们自己记录和运行Plone测试,如果有人知道这种类型的项目,我真的很想参与在其发展中。

+0

好吧,我害怕因此,如何以编程方式驾驶硒你。功能测试代码并预览浏览器中的每一步(使用pdb设置)? – 2012-04-24 12:55:10

+0

使用与Selenium的Python绑定非常简单:http://pypi.python.org/pypi/selenium – 2012-04-26 12:40:09

+0

我非常了解绑定,希望在问题的背景下有所收获重刑,标记点。 – 2012-04-26 17:01:10