B2G/QA/Automation/Style Guide/Python Script Style
Python Script Style Guide
- Module names should be called test_ and then behavioral areas.
- Test method names should always show the intent of the test case.
# Good def test_that_advanced_search_does_not_find_item(self): # Bad def test_advanced_search(self):
- Each file should have a completed copy of the MPL2 license block, immediately followed by an empty line.
- Each file should pass PEP8 except for line length, see below.
# Good def method(self, parameter) # Bad def method(self,parameter)
- Lines should try not to have more than 100 characters.
- Docstrings should conform to PEP0257 and should be on a single line wherever possible.
# Good def click_login(): """Clicks the login link.""" # Bad def click_login(): """ Clicks the login link. """
Where not possible, the first line should be a summary.
# Good def login(): """Logs in. Clicks the login link and then waits for the home page to load. """ # Bad def login(): """Logs in. Clicks the login link and then waits for the home page to load."""
- Indenting should be a soft tab (4 spaces) as common with in Python. Do not mix tabs and spaces!
- There should be no whitespace at the end of the file (as per PEP8).
- Comments should be on the line above. Remember to update comments when changing code so that code matches the comments.
- Comments should be used carefully. Try not to put comments that don't provide any additional value
- Class names should be in Pascal style as this is Python idiomatic.
# Good class TestThisSite: # Bad class test_this_site:
- Locator variables should be prefixed with
_to show that it is private.
- Variables should be descriptive of the area and not clash with any properties.
- Should have a suffix of
- Accessing locators should be done through a property or method as this keeps the locator as read-only.
@property def search_term(self): return self.marionette.find_element(*self._search_box_locator).value
- We should use locators in the following order of preference (there will be exceptions):
- Class name
- CSS selector
- CSS locators should use whitespace for readability when using direct descendants.
# Good _my_locator = "css=#content > p > a" # Bad _my_locator = "css=#content>p>a"
- Use Python tuples to define locators:
# Good _my_locator = (By.ID, "content")
- Methods that perform actions on the page should indicate the action in the method name.
# Good def click_report_with_length(length) # Bad def report_length(length)
- Actions should wait for the appropriate action to complete. This could be an implicit or explicit wait. For example, clicking a login button might explicitly wait for a username field to be visible.
Using High-Level Methods
- A test script should remain a short entry point which leads to more details (like the main() function of a program). Don't detail every single tap and wait, in the test. Use methods that reflect a user's intent rather than a sequence of small steps the user has to do. The details of how to fulfill that intent will be in the page classes.
# Good messages.send_an_sms_to_yourself() # Bad messages.open_sms_app() messages.create_new_sms(receiver) messages.type_content(content) messages.send()
- Name your variables with units.
# Good timeout_in_seconds, width_in_pixels # Bad timeout, width
- Don't shorten variable names.
# Good self.time_out_error_time = 1000 # Bad self.t_out_err_tim = 1000
Making Tests Locale Independent
It is recommended to make the test scripts (and especially the helper methods) locale independent, because this enables us to test devices in RTL locale. There are still some sections in gaiatest where it checks for the displayed English text, but unless one cannot avoid it, data-l10n-id attribute should be checked in place of any raw text comparison.
# below method will not work in a non-English locale, # should only be used when there is no appropriate tag for the Open App button button = self.root_element.find_element(*self._install_button_locator) Wait(self.marionette).until(lambda m: button.text == 'Open app') button.tap() # below code will tap 'Send Mozilla Feedback' button in any locale _send_feedback_locator = (By.CSS_SELECTOR, '[data-l10n-id="sendMozillaFeedback"]') element = Wait(self.marionette).until( expected.element_present(*self._send_feedback_locator)) Wait(self.marionette).until(expected.element_displayed(element)) element.tap()
The rules of thumb are:
- We can check for the raw text in order to verify the user input (i.e., a phone number entered in the dialer app)
- We check the data-l10n-id for any string that comes from Gaia only (i.e., an error message)