In our code so far, we have used many locators to find specific elements, whether they are apps (iFrames) or specific parts of apps. Up to now we have been writing locators directly inline and duplicating code, as a result. To improve the situation further, it is a good practice to abstract these locators out into Python tuple variables so they can be reused. In this article we'll show you how.
Tuples, and the Marionette By class
As an example, consider the locator that we have been using to locate the Contacts app iFrame:
'css selector', "iframe[data-url*='contacts']"
We use this locator both when waiting for the frame to be displayed and when we switch into it. To make things easier we can store this in a variable (we also need to import
from marionette import By _contacts_frame_locator = (By.CSS_SELECTOR, "iframe[data-url*='contacts']")
By class provides a shortcut to accessing the locating techniques like
id, CSS selector and so forth. We grab the element using the selector like before, and then store it in a CSS tuple variable. If the HTML (and your locator) changes, then it is easier to just update the variable once, rather than making changes in two places. To use this tuple you include it in your
find_element() method like so:
* — in this context — is Python code for unpacking an argument list; it is splitting the original tuple into the two arguments that we need to pass into
find_element(). For more information and examples, read Unpacking argument lists in the Python docs.
Another tuple example, which locates by
id attribute, is as follows:
_add_contact_button_locator = (By.ID, 'add-contact-button')
Using tuples and By in our Contacts test
Now it's time to reduce duplication in our
test_add_contact.py testcase by moving the locators out of the test and into the scope of the
TestContacts class where they can be shared. We'll show you how to substitute a couple of the locators, and then leave the rest as a reader exercise.
First of all you need to make sure you import
By, by putting the following at the top of your code:
from marionette import By
Now we can add our tuples at the top of the
TestContacts class; add the following lines just underneath the
class TestContacts(unittest.TestCase): line:
_contacts_frame_locator = (By.CSS_SELECTOR, "iframe[data-url*='contacts']") _save_button_locator = (By.ID, "save-button")
Now you can go through your code and replace all instances of
and all instances of
find_element('css selector', "iframe[data-url*='contacts']")
And that's it for now. We are sure that you'll already be able to see the benefits of this code reuse, even in this simple example. This technique starts to become particularly effective as you start to write more complex tests that may have that same locator used 5, 10 or 20 times.