Part 6: Using tuples, and Marionette's By class

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 By):

from marionette import By

_contacts_frame_locator = (By.CSS_SELECTOR, "iframe[data-url*='contacts']")

The Marionette 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:


Note: The * — 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 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

find_element('id', 'save-button')



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.