QA/Execution/Web Testing/Docs/Automation/Testcases/Guidelines: Difference between revisions

No edit summary
 
Line 1: Line 1:
Testcase guidelines
#REDIRECT [[QA/Execution/Web_Testing/Docs/Automation/StyleGuide]]
 
Here be some Selenium guidelines (mostly for writing, but include running here too).
 
= Setup information =
 
See Raymond's documentation, here: https://intranet.mozilla.org/User:Retornam@mozilla.com/Selenium/#qa-selenium.mv.mozilla.com
 
= Pre-checkin requirements  =
 
#Testcase/suite naming structure: <br>
##Give meaningful names to testcases (follow the [http://www.python.org/dev/peps/pep-0008/ python PEP 8 Style Guide])<br>
###Testcase file/module name:&nbsp; e.g.: test_collections_add_new_collection.py (Format: test_module_name_of_test_case, lower_case_with_underscores<br>)
###Suite: suite_Collections.py ("suite" is required and standard; _Collections.py is whatever area you're writing it for)
###Don't use hyphens, as those are illegal in Python class names<br>
###Class name&nbsp;:&nbsp;CapWords, eg. class ForumReply<br>
###function names: Function names should be lowercase, with words separated by underscores as necessary to improve readability eg. def test_forum_reply_logged_out<br>
#Have the testcase reviewed by a peer, following these requirements
#Comment briefly about the flow and aim of the testcase, as well as commenting inline
#Do not use xpath if possible. Use css locators
#If you're using a regular expression, comment before using it for what you're looking
#Add-on IDs are different between production and preview, so if your testcase uses a specific add-on ID, make sure to note that at the top of the testcase in the comment section
#Use the [https://bugzilla.mozilla.org/show_bug.cgi?id=576197 page model object]
##how to use the page object model:
###&nbsp;page.py - The base class for all pages. It has the most common functions<br>that need to be performed on any web page. All other individual page classes<br>will be derived (parent - child) from page.py for any web app (AMO/SUMO)
###&nbsp;sumo_page.py or amo_page.py: This page class will have all the web elements that are common across all web pages of sumo/amo. For eg. header/footer links, login/logout links that appear on all pages.
###support_home_page.py - child class of sumo_page.py. All web elements on the<br>Support Home page (title,main_search_box,search_button) are defined as class<br>variables. All operations that can be performed on these elements are defined<br>as functions<br>(click_log_in_link, do_search_on_main_search_box)
###&nbsp;main test case (test_search_on_home_page.py) - In the main TC you import<br>those pages that will be hit. In this test case we will be hitting Support<br>Home Page, Search page, Advanced Search page &amp; Login page.
###Advantages:<br>1. You don't have a functions library (AMO_functions/Sumo_functions) which<br>grows over time. You have the functions spread over different page classes.<br>2. Main test case looks cleaner. In the traditional model a sample code looks<br>like:<br> sel.open("/en-US/kb/")<br> sel.type("fsearch-new", "iphone")<br> sel.click("searchsubmit-new")<br> sel.wait_for_page_to_load("30000")
###With the page model object, the same code in the TC will look like: support_home_page_obj.go_to_support_home_page(sel) <br>support_home_page_obj.do_search_on_main_search_box(search_term,search_page_obj,sel)
###Disadvantages:<br>1. The selenium object gets passed around twice, from main test case -&gt;<br>individual page class -&gt; parent page class<br>2. Too many imports in the TC<br><br>
 
= Testcase template / structure of a page object model <br> =
<pre>@author: mozilla
'''
from selenium import selenium
import vars
import unittest
import support_home_page
import search_page
import refine_search_page
import login_page
class SearchOnHomePage(unittest.TestCase):
 
 
    def setUp(self):
        self.selenium = selenium(vars.ConnectionParameters.server, vars.ConnectionParameters.port, vars.ConnectionParameters.browser, vars.ConnectionParameters.baseurl)
        self.selenium.start()
        self.selenium.set_timeout(vars.ConnectionParameters.page_load_timeout)
        self.selenium.open(vars.ConnectionParameters.authurl)
        self.selenium.open(vars.ConnectionParameters.authurlssl)
 
 
    def tearDown(self):
        self.selenium.stop()
 
 
    def test_search_on_home_page(self):
        sel = self.selenium
        support_home_page_obj = support_home_page.SupportHomePage()
        search_page_obj      = search_page.SearchPage()
               
        search_term = 'iphone'
        support_home_page_obj.do_search_on_main_search_box(search_term,search_page_obj,sel)
 
        url = search_page_obj.get_url_current_page(sel)
        self.failUnless(search_term in url, "Search term %s does not exist in the url %s" %(search_term,url))
       
    def test_search_advanced(self):
        uname      = ''
        pwd        = ''
        search_term = 'iphone'
        sel        = self.selenium
       
        login_page_obj        = login_page.LoginPage()
        support_home_page_obj  = support_home_page.SupportHomePage()
        refine_search_page_obj = refine_search_page.RefineSearchPage()
        search_page_obj        = search_page.SearchPage()
       
        login_page_obj.log_in(uname, pwd, sel)
       
        support_home_page_obj.go_to_support_home_page(sel)
        support_home_page_obj.click_advanced_search_link(refine_search_page_obj, sel)
       
        refine_search_page_obj.do_search_on_knowledge_base(search_term, search_page_obj, sel)
        url = refine_search_page_obj.get_url_current_page(sel)
        self.failUnless(search_term in url, "Search term %s does not exist in the url %s" %(search_term,url))
       
 
if __name__ == "__main__":
    unittest.main()
 
</pre>
 
= Some good practices =
 
* It's best to check if you are logged in at the beginning of every test case. At the end of the test case, always logout.
 
Example: StoreElementpresent link=log in not_logged_in
 
gotoIf ${not_logged_in}==true target
 
clickAndWait link=Log out
 
label target . .
 
= Run scripts on all environments =
* If you want to run your scripts on all environments, then code your setUp() function like this:
      def setUp(self):
      self.verificationErrors = []
      self.objConnection = GridConnection.GridConnection()
      for envCounter in self.objConnection.remoteControls:
          self.selenium = selenium(self.objConnection.server,self.objConnection.port,envCounter,self.objConnection.amoStaging)
          #self.selenium = selenium('localhost',4444,"*firefox","http://preview.addons.mozilla.org")
          self.selenium.start()
 
=IDE=
* click vs. clickAndWait - use the latter on anything that requires network activity, but if you use the former on other buttons/links, your script will pause indefinitely
* verifyLocation - use w/regexpi; otherwise it'll be absolute (I think)
** e.g. ...
* wherever possible, use relative--not absolute--URLs in |open| commands
 
*Store xpath you use in the testcase in variables at the beginning of the testcase and use the variable whenever you need to use the xpath.This way,if you ever need to change the xpath,you change it only once at the top of the testcase.
 
* storeLocation/verifyLocation usage:
** after a storeLocation, verifyLocation by:
*** verifyLocation regexpi:.*/en-US/firefox/admin.* (e.g.)
 
*Add an echo statement after every assertion.
 
*StoreXpathCount  command can be used to find the number of elements having a similar xpath.
 
*VerifySelectedLabel can be used to verify the selected option in a dropdown box.
 
= Locating elements  =
 
*Locating elements using Selenium locators
**xpath: for elements that have dynamically generated xpaths use 'contains'.
 
  for e.g. if xpath for 'remove add-on' link has the add-on ID
in it (id('addon-2464')/a[1]) and we do not know the add-on ID then
we can write the xpath with 'contains'
//div[@id,'addon-2464'][2]/a[1]) =&gt; //div[contains(@id,'addon- ')][2]/a[1]
 
**If you want to match on the text in an element
 
  //a[text()=’My Text’]: matches a link whose text is “My Text”
 
**If you want to match more than one string in attribute or text
 
  //a[contains(text(),’Bill’) &amp;&amp; contains(text(), ‘Clinton’)]:matches a link
whose text contains both Bill and Clinton. You can use any xpath function or
operator to create your expression 
 
**Find the nth element of a type
 
  //div[position()=3]: matches the third div on the page
//div[@id=’abc’]//table[position()=3]: matches the third table inside the div
whose id is abc
 
<br>
 
= An exception to an exception  =
 
Sometimes it's preferable to avoid an exception for a specific situation in a test case. Since it can take awhile for bugs to be fixed, it can be benefical to suppress exceptions related to known bugs to avoid test suites that finish red but don't provide new failure information. The "exception to an exception" is narrowly defined to correspond to a condition which typically is described in a filed bug.
 
For example, a search fails for a particular add-on. Troubleshooting finds that the add-on is special in a way which is either is incorrect, or the web pages don't react to it appropriately. A bug is filed but the fix has a low priority. The test case is modified to skip the add-on.
 
== Minimize how much verification is skipped  ==
 
The change to be test should be as simple as possible and minimize how much verfication is skipped. For example, a test case for an add-on layout page experiences an exception on a particular add-on's description. The modification should not skip checking descriptions for all add-ons, nor skip the add-on altogether if there are other independent values which can still be verified. In the test case, the description's exception logic is enhanced to also check the description and/or add-on id as per the Bugzilla bug. The exception is "suppressed" if it fits the condition in the bug, otherwise the exception is treated like any other exception.
 
== More about the test case modification  ==
 
*When the test case suppresses an exception, or part of a test is skipped, a message is printed to the console to provide quiet visibility. The console message includes the particular item being skipped, the condition, and the bug#.
*The exception to the exception is documented in the test case source code with a comment containing the word 'bugzilla' and the bug number. The 'bugzilla' keyword can be used later in source code searches.
 
== Bugzilla comment  ==
 
*Add a comment the the bug that includes the test case module that's modified and a short description of criteria for skipping the exception. Recommend beginning the comment with "for WebQA".
Confirmed users
9,511

edits