Мой взгляд на "хорошие" локаторы в Selenium

Пост о том, как я пересмотрел свое отношение к локаторам и при этом никто не пострадал. Всем начинающим автоматизаторам советуют изучать css и xpath локаторы. Без знания локаторов сложно писать UI автотесты. Большинство курсов, книг и статей глаголят, что вот, мол, нужно уметь писать "хорошие" локаторы. При этом понятие "хороший локатор" не имеет четкого опеределения.

Давайте посморим, например, какой из этих локаторов "хорош"?

1) #push > article:nth-child(1) > div
2) //*[@id="push"]/article[1]/div

Считаю, что вариант #1 лучше, так как он более лаконичный, да и вообще я предпочитаю использовать css локаторы вместо xpath. Почему?

Еще пример:

1) article.post
2) //article[@class='post ']

Здесь четко видно, что css более читабельный и писать его проще. Вывод: css локаторы более предпочтительны для использования в наших Selenium тестах. Казалось бы, вопрос можно закрывать и расходиться делать проекты, но мы же так и не определили понятие "хороший локатор". Приведенные выше локаторы хороши, но достаточно ли, чтобы претендовать на звание "лучший локатор"?

Когда я только начинал заниматься автоматизацией, следуя всем наставлениям своих менторов, старался учить и писать максимально хорошие локаторы. Радовало, что в то время в файрфоксе еще работали Firebug и FirePath. Но проект мой открывался только в IE8 и мне такие блага были недоступны. Поэтому я работал в убогом Internet Explorer Dev Tools и тогда еще парился по поводу "хороших" локаторов. После у меня был проект, на котором UI рендерился с помощью фреймворка Vaadin. Тогда я не стеснялся просто скопировать локатор, который мне предлагал FirePath и без оглядки его использовать. Но вопрос "хороших локаторов" по-прежнему не давал покоя.

Локатор получался местами длинным:

.v-verticallayout.v-verticallayout-invoice-detail-view-main.invoice-detail-view-main

или

//*[@id='ordermgmtui-1932176413']//table/tbody/tr[2]/td[@class='v-formlayout-contentcell']/input

Но эти локаторы были хороши, потому что работали стабильно. Почему я не сильно старался сделать их лучше или переписать? А зачем? Они дались мне практически даром: два клика в браузере и все. Важно понимать, что локаторы - это не нечто архиавжное. Иногда говорят: "Мы так пишем локаторы, чтобы тесты были стабильнее, вдруг завтра верстка поменяется…​" Если верстка поменяется, вы сделаете два клика и поменяете локатор. Нет смысла сейчас тратить 5-10 минут на то, что может никогда и не случиться.

Именно поэтому я перестал обращать внимание на возгласы по поводу правильных и красивых локаторов. Правый клик мышкой на элементе → Copy selector и поехали.

Пример c http://inbox.google.com:

def setup_module(m):
    (LoginPage()
        .open()
        .login_as(email,password))

def test_can_add_reminder():
    (MainPage()
     .add_new_reminder("call Mama")
     .reminder.should(have.text("Call Mama")))

Вполне читабельные тесты.

Реализации PageObject:

from selene.api import *

class LoginPage(object):
    def __init__(self):
        self.email = s("#Email")
        self.next_btn = s("#next")
        self.password = s("#Passwd")
        self.sign_in_btn = s("#signIn")
        self.city_input = s("#answer")
        self.ready_btn = s("#submit")


    def open(self):
        browser.visit("https://inbox.google.com/")
        return self

    def login_as(self, email, password):
        self.email.send_keys(email)
        self.next_btn.click()
        self.password.send_keys(password)
        self.sign_in_btn.click()

class MainPage(object):

    def __init__(self):
        self.plus_btn =  s("#aVMuZe > div.nPQzwd.iP > div.jB > button")
        self.create_new_reminder_btn = s("#aVMuZe > div.nPQzwd.iP > div.jp > div.S > button")
        self.reminder_input = s("body > div.eG.f > div > div.nY.na > div.od > div.k0 > div")
        self.reminder_save_btn = s("body > div.eG.f > div > div.nY.na > div.od > div.ft > div.dy.qj.ev")
        self.reminder = s("#Nr > div > div > div.No > div.bg.summTaskTitle > span")


    def add_new_reminder(self, text):
        self.plus_btn.hover()
        self.create_new_reminder_btn.click()
        self.reminder_input.send_keys(text)
        self.reminder_save_btn.click()
        return self

Да, некоторые локаторы длинные и нечитабельные, но они "хорошие", потому что они работают стабильно и дались мне практически даром.

Вывод: Для меня "лучший локатор" - тот, который работает стабильно и на создание которого я потратил максимум 10 секунд. Из личных наблюдений: Chrome Dev Tools позволяет в 80% случаев получать именно такие локаторы. Поэтому я не стесняюсь просто брать и копировать локатор прямо оттуда. Мой опыт показывает, что гораздо важнее уметь быстро создавать тесты, чем изначально тратить кучу времени на локаторы, которые вы все равно рано или поздно будете менять. И все же обязательно нужно думать головой и принимать только взвешенные решения.