Перейти к содержимому

Фотография

Использование ООП в автотесте

python selenium webdriver

  • Авторизуйтесь для ответа в теме
Сообщений в теме: 6

#1 anaxaim

anaxaim

    Новый участник

  • Members
  • Pip
  • 6 сообщений

Отправлено 11 ноября 2014 - 20:27

Пишу автотест и хотелось бы объединить методы класса с одинаковыми параметрами в один метод (или несколько методов), а то код получается очень громоздким и объектно-ориентированный подход теряет смысл.
 
Есть класс локаторов и класс действий с локаторами, можно ли избавиться в классе MainPage(BasePage)
от одинаковых конструкций, меняя лишь локатор и группируя одинаковые методы по работе с локаторами. Методы из класаа MainPage вызываются в другом основном классе.
 
Подскажите как их объединить? 
 
class MainPageLocators(object):
    login_line = (By.ID, 'loginName')
    password_line = (By.ID, 'loginPass')
    enter_button = (By.ID, 'logButton')
    worker_button = (By.XPATH, "//span[contains(@class, 'navigation-LeftNavigation__row') and @title='Сотрудники']")
    x_link = (By.ID, 'ourOrg')
    our_company_button = (By.XPATH, "//div[@title='Наша компания']")
    search_name = (By.ID, 'fld-ПоискСотрудникаПоЮрЛицам')
    record = (By.ID, 'userName409')
    close = (By.XPATH, "//div[@type='Control/Button' and @sbisname='windowTitleClose']")
    initial_button = (By.XPATH, "//div[contains(@class, 'username asLink username__link')]")
    
class BasePage(object):
    def __init__(self, driver):
        self.driver = driver
class MainPage(BasePage):
    def login(self):
        element = self.driver.find_element(*MainPageLocators.login_line).send_keys('check_rigth_user')
    def password(self):
        element = self.driver.find_element(*MainPageLocators.password_line)
        element.send_keys('qwerty123')
        
    def enter_button_click(self):
        element = self.driver.find_element(*MainPageLocators.enter_button)
        element.click()
    
    def worker_button_click(self):
        element = self.driver.find_element(*MainPageLocators.worker_button)
        element.click()
    def x_link_click (self):
        element = self.driver.find_element(*MainPageLocators.x_link)
        element.click()
    def our_company_button_click (self):
        element = self.driver.find_element(*MainPageLocators.our_company_button)
        element.click()
    def name_for_search(self):
        element = self.driver.find_element(*MainPageLocators.search_name)
        element.send_keys('Белова Олеся Александровна')
    def record_click (self):
        element = self.driver.find_element(*MainPageLocators.record)
        element.click()
    def close_click (self):
        element = self.driver.find_element(*MainPageLocators.close)
        element.click()
    def initial_click (self):
        element = self.driver.find_element(*MainPageLocators.initial_button)
        element.click()

  • 0

#2 barancev

barancev

    Администратор

  • Admin
  • PipPipPipPipPipPip
  • 6 872 сообщений
  • ФИО:Алексей Баранцев
  • Город:Россия, Москва


Отправлено 12 ноября 2014 - 08:13

Не очень понятно, какого эффекта хочется добиться (то есть как должен выглядеть по Вашему мнению улучшенный код, но могу предложить два варианта:

1) Не делать методы типа enter_button_click, вместо этого сделать побольше выскоуровневых методов типа login, тем самым работа с элементами будет скрыта в объекте-странице.
Пример такого подхода к проектированию можно посмотреть тут: https://github.com/d...registration.py

2) Описывать элементы не как поля, а как properies
Пример такого подхода к проектированию можно посмотреть тут: описание элементов страницы и использование этих элементов


  • 1
Алексей Баранцев
Тренинги для тестировщиков (тестирование производительности, защищенности, тест-дизайн, автоматизация):
Линейка тренингов по Selenium

#3 polus

polus

    Новый участник

  • Members
  • Pip
  • 63 сообщений
  • ФИО:Поляруш Михаил Анатольевич
  • Город:Kiev


Отправлено 12 ноября 2014 - 08:14

Первый вариант - это можно сделать через миксование классов, так будет меньше всего переделок в коде. Узнать более подробно об этом можно по ссылке

from selenium.webdriver.common.by import By
from selenium import webdriver
class MainPageLocators(object):
    login_line = (By.ID, 'loginName')
    password_line = (By.ID, 'loginPass')
    enter_button = (By.ID, 'logButton')


class BasePage(object):

    def __init__(self, driver):
        self.driver = driver


class MainPage(BasePage, MainPageLocators):

    def login(self):
        element = self.driver.find_element(*self.login_line).send_keys('check_rigth_user')

    def password(self):
        element = self.driver.find_element(*self.password_line)
        element.send_keys('qwerty123')

    def enter_button_click(self):
        element = self.driver.find_element(*self.enter_button)
        element.click()

page = MainPage(webdriver.Firefox())
print page.driver
print page.login_line
print page.login

Ну а во вторых, почему бы не соединить локаторы и действия в один класс ?! 

from selenium.webdriver.common.by import By
from selenium import webdriver

class BasePage(object):

    def __init__(self, driver):
        self.driver = driver


class MainPage(BasePage):
    login_line = (By.ID, 'loginName')
    password_line = (By.ID, 'loginPass')
    enter_button = (By.ID, 'logButton')

    def login(self):
        element = self.driver.find_element(*self.login_line).send_keys('check_rigth_user')

    def password(self):
        element = self.driver.find_element(*self.password_line)
        element.send_keys('qwerty123')

    def enter_button_click(self):
        element = self.driver.find_element(*self.enter_button)
        element.click()

page = MainPage(webdriver.Firefox())
print page.driver
print page.login_line
print page.login

  • 1

Практикующий консультант по автоматизации тестирования ПО и тренер

Портал по автоматизации тестирования ПО http://automated-testing.info

Онлайн обучение автоматизации тестирования http://lessons2.ru

Персональные консультации и менторинг SDConsulting

Личный сайт http://poliarush.com


#4 barancev

barancev

    Администратор

  • Admin
  • PipPipPipPipPipPip
  • 6 872 сообщений
  • ФИО:Алексей Баранцев
  • Город:Россия, Москва


Отправлено 12 ноября 2014 - 08:51

Миша, не нужно публиковать скрытую рекламу как ссылки на якобы информационные материалы. Пиши честно -- "про это я рассказываю в своём тренинге, вот тут". За скрытую рекламу буду наказывать.


  • 0
Алексей Баранцев
Тренинги для тестировщиков (тестирование производительности, защищенности, тест-дизайн, автоматизация):
Линейка тренингов по Selenium

#5 anaxaim

anaxaim

    Новый участник

  • Members
  • Pip
  • 6 сообщений

Отправлено 12 ноября 2014 - 12:56

Не очень понятно, какого эффекта хочется добиться (то есть как должен выглядеть по Вашему мнению улучшенный код, но могу предложить два варианта:

1) Не делать методы типа enter_button_click, вместо этого сделать побольше выскоуровневых методов типа login, тем самым работа с элементами будет скрыта в объекте-странице.
Пример такого подхода к проектированию можно посмотреть тут: https://github.com/d...registration.py

2) Описывать элементы не как поля, а как properies
Пример такого подхода к проектированию можно посмотреть тут: описание элементов страницы и использование этих элементов

 

Спасибо Вам и Михаилу. А не могли бы вы посмотреть код самого теста и подсказать, что в нем можно изменить или добавить. Стоит ли разбивать метод test_new на отдельные мини-тесты (методы) или оставить один? Как лучше выполнять проверки assert (правильно ли я их сделал)? Правильно ли организована работа с ожиданиями или лучше применять Implicit waits? 

class main(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Firefox()

    def test_new(self):
        self.driver.get('https://fix-inside.tensor.ru')
        main_page = MainPage(self.driver)
        main_page.login()
        main_page.password()
        main_page.enter_button_click() #нажимаем кнопку Войти 
        #при переходе на главную страницу ждем загрузку таблицы новостей
        try:
            news = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "table.news-PreprocessorNews-main_page")))    
        except:
            pass
        assert 'news', "Переход на стартовую страницу отменен"

        #нажимаем на ссылку Сотрудники
        main_page.worker_button_click()
        #при переходе по ссылке Сотрудники ждем загрузку полей с именами сотрудников
        try:
            worker_table = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.ws-relativegrid-cellwrapper ws-relativegrid-firefox_100_height")))    
        except:
            pass
        assert 'worker_table', "Переход в раздел сотрудники не состоялся"

        #кликаем по ссылке в шапке страницы
        main_page.x_link_click()
        #при переходе по ссылке ждем загрузки формы (локация по селектору - строчный элемент "Выберите организацию")
        try:
            form_open = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "span.ws-float-area-title-style")))    
        except:
            pass
        assert 'form_open', "Форма не открылась"

        #Выбираем "Наша компания"
        main_page.our_company_button_click()
        #assert 

        #Ищем сотрудника Белова Олеся Александровна
        main_page.name_for_search()
        #Ждем появления ссылки с фамилией запращиваемого сотрудника
        try:
            record = WebDriverWait(self.driver, 5).until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.ws-ellipsis listEmpl-fio")))    
        except:
            pass
        assert 'record', "Запись не найдена"

        #Кликаем на записаь в таблицу
        main_page.record_click()
        #при открытии формы ждем ее полной загрузки и появления локатора - строчного элемента "Белова"
        try:
            card = WebDriverWait(self.driver, 5).until(EC.presence_of_element_located((By.CSS_SELECTOR, "span.ws-editAtPlace-inner")))    
        except:
            pass
        assert 'card', "Карточка не открылась"

        #Закрываем форму, крестик пропадает
        main_page.close_click()
        assert 'card', "Карточка не закрылась"

        #кликаем на ссылку с инициалами
        main_page.initial_click()
        #открывается меню со ссылками на другие разделы сайта
        try:
            menu = WebDriverWait(self.driver, 1).until(EC.presence_of_element_located((By.ID, "adminlinks")))    
        except:
            pass
        assert 'menu', "Меню не открылось"

        #main_page.quit_button_click()
        quit_click = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "#headerLeft > div.big.auth > div.log > div"))) 
        quit_click.click()
        #проверка выхода с сайта по локации кнопки Вход
        try:
            last_check = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "logButton")))
        except:
            pass
        assert 'ast_check', "Выход не удался"

        
            
    #def tearDown(self):
    #    self.driver.close()

if __name__ == "__main__":
    unittest.main()

  • 0

#6 barancev

barancev

    Администратор

  • Admin
  • PipPipPipPipPipPip
  • 6 872 сообщений
  • ФИО:Алексей Баранцев
  • Город:Россия, Москва


Отправлено 12 ноября 2014 - 13:31

Почему маленькие авто-тесты лучше, чем большие?

 

1) Из отчёта сразу видно, что работает, а что нет (особенно если тестам дать хорошие имена)

2) Если один тест упал -- у остальных есть шанс отработать, так что за один проход мы получаем больше информации

3) При разработке (при отладке) или при воспроизведении сбоя проще работать с маленьким тестом

4) Можно мелкие тесты произвольным образом группировать, распараллеливать, в общем, как-то управлять тестовым набором

 

Разумеется, если у вас сложный сценарий, который на мелкие не разбивается -- тогда вопрос лишён смысла. Но везде, где можно разбить -- стоит это сделать.

 

Однако не следует впадать в противоположную крайность. Иногда советуют придерживаться правила "в одном тесте одна проверка", и тогда, например, если нам надо в какой-нибудь форме проверить, что пять полей являются обязательными -- надо делать пять отдельных тестов. Вовсе нет. Проверок в тесте может быть и несколько. Важно постараться не дублировать сценарии (действия).

 

Но даже в случае большого сценария, конечно, код стоит декомпозировать, выделить небольшие вспомогательные методы (не тестовые, а обычные), и в тесте обращаться к этим методам.

 

То есть этот длинный тестовый метод мог бы выглядеть так:

    def test_new(self):
        main_page = self.openMainPage()
        main_page.login("admin", "password")

        workers_page = main_page.open_workers_page()
        workers_page.open_search_form()
        workers_page.select_organization("Наша компания")
        workers_page.search_worker("Белова Олеся Александровна")
        worker_page = workers_page.open_worker_card(0) # открываем первую из найденных карточек
        workers_page.close_search_form()

        worker_page.open_menu()

        main_page.logout()

а все подробности скрыты во вспомогательных методах. И тогда тестовый метод легко читать и понимать, даже никаких комментариев в коде не надо.


  • 0
Алексей Баранцев
Тренинги для тестировщиков (тестирование производительности, защищенности, тест-дизайн, автоматизация):
Линейка тренингов по Selenium

#7 barancev

barancev

    Администратор

  • Admin
  • PipPipPipPipPipPip
  • 6 872 сообщений
  • ФИО:Алексей Баранцев
  • Город:Россия, Москва


Отправлено 12 ноября 2014 - 14:45

Как лучше выполнять проверки assert (правильно ли я их сделал)? Правильно ли организована работа с ожиданиями или лучше применять Implicit waits?

 

1. Во-первых, сейчас Ваши проверки не проверяют ничего :)

Потому что, например, вместо

assert 'menu'

нужно было бы написать

assert menu

Если это понятно -- можно двигаться дальше.

 

2. Автотесты обычно пишутся исходя из предположения, что они большую часть запусков будут успешными, поэтому перехватывать каждое исключение особого смысла нет. Упадут -- будем разбираться. Видно же, в какой строчке упало. Иными словами, вместо

try:
    menu = WebDriverWait(self.driver, 1).until(EC.presence_of_element_located((By.ID, "adminlinks")))    
except:
    pass
assert menu, "Меню не открылось"

можно написать просто

menu = WebDriverWait(self.driver, 1).until(EC.presence_of_element_located((By.ID, "adminlinks"))) 

Да, отчёт будет чуть менее красивым (без пояснения "Меню не открылось"), но если это всё упадёт во вспомогательном методе, который называется, скажем, openMenu -- и так будет понятно, что меню не открылось.

 

3. Ожидания нормальные, тут всё в порядке, но если Вы везде используете только presence_of_element_located -- тогда может быть и правда стоит перейти на использование неявных (implicit) ожиданий.


  • 1
Алексей Баранцев
Тренинги для тестировщиков (тестирование производительности, защищенности, тест-дизайн, автоматизация):
Линейка тренингов по Selenium



Темы с аналогичным тегами python selenium webdriver

Количество пользователей, читающих эту тему: 0

0 пользователей, 0 гостей, 0 анонимных