Спасибо огромное, было бы здорово взглянуть И вот это еще, если будет время, пожалуйста - “С рефлексией все будет гораздо компактней. Может на днях залью новую версию.” - Какой должна быть идеальная структура проекта автотестов? - #33 от пользователя adv
Ну у меня из-за специфики проекта, как плюс это считаю, повторяются многие элементы и локаторы на страницах, но может быть такое что в разных блоках страницы то уже структурирую по компонентам и степам к ним и объединяю в страницы это всё чудо.
Т.к. есть кастомные решения для выпадаек и для кнопок, то есть объекты со специфическими методами для их манипуляций.
public class ProductButton extends AbstractElement{
@FindBy(css = "a.ga-add-to-cart")
public SelenideElement buttonAddToCart;
@FindBy(css = "a.button-preorder")
public SelenideElement buttonPreOrder;
@FindBy(css = "span.in-cart > a")
public SelenideElement buttonInCart;
@FindBy(name = "btn-minus")
public SelenideElement buttonMinus;
@FindBy(name = "quantity")
public SelenideElement fieldQuantity;
@FindBy(name = "btn-plus")
public SelenideElement buttonPlus;
@FindBy(css = "span.icon")
public SelenideElement labelStockWithIcon;}
Это всё может находится на разных страницах и может в разных блоках, поэтому есть такая штука в объекте какого-то блока
@FindBy(css = "div.product-button-wrapper") public ProductButton productButton;
Далее, эту кнопку мне нужно найти для определённого товара на странице по id, к примеру на странице категорий. А страница категорий состоит из “строк/блоков” каждой единицы товара и чтобы взять определённого товара блок и только в нём работать есть акая штука:
public class ProductList extends AbstractElement {
public ProductsLineBlock findProductByIDInTags(String id, String tagName) {
return initContainer($(String.format("%s[data-id='%s']", tagName, id)),
ProductsLineBlock.class);
}
}
Именно так потому что вид страницы может быть в виде таблицы, строк с расширенным описанием или строк без описание. И таким способом я дальше могу работать с локаторами которые содержатся в ProductsLineBlock. В котором есть особенная кнопка “Купить”
Вот что содержится в AbstractElement
public abstract class AbstractElement extends ElementsContainer{
protected <T extends AbstractElement> T initContainer(SelenideElement element, Class<? extends AbstractElement> type) {
T result;
try {
result = (T) type.newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to create elements container for element", e);
}
PageFactory.initElements(new SelenideFieldDecorator(element), result);
result.setSelf(element);
return result;
}
Вот к примеру кастомная выпадайка и метод для неё
public class CustomDropdown extends AbstractElement {
public void selectItem(String nameOfItem){
getSelf().click();
$(byXpath(String.format("//span[.='%s']", nameOfItem))).scrollTo().click();
}}
Таких кастомных 3 штуки есть, + есть аналог для номера телефона и выбора кода страны, составной элемент с инпутом и выпадайкой
Также есть кастомные элементы это поиск, у него есть выпадайка с перечнем категорий в которых искать, + составной из двух блоков быстрый поиск.
Это всё классы компоненты страниц и из них я уже леплю степы для них, которые объединяю в пакеты страниц.
Пример теста: public class TestTest extends BaseTest {
@Test
public void olo(){
Configuration.baseUrl = TestData.urlMainPageGSMCom;
addProductViaAjaxUrl(simpleProduct);
open(urlCheckout);
CheckoutContactInformationSteps checkoutContactInformationSteps = getStepsFrom(CheckoutContactInformationSteps.class);
checkoutContactInformationSteps.testMethod();
}
}
Где getSteps это:
protected static <T> T getStepsFrom(Class<? extends AbstractSteps> pageObjectClass) {
return (T) page(pageObjectClass);
}
ну и testMethod
public CheckoutContactInformationSteps testMethod(){
this.checkoutContactInformation.customCountry.selectItem(TURKEY);
this.checkoutContactInformation.customPhone.setPhoneByCountry(TURKEY, TestData.CONTACT_NUMBER);
return this;
}
аа ну для этого делаю сайта автоматизацию: gsmserver.com, можете увидеть какая кнопка “Купить” и “Поиск” о которых говорил
И метод getStepsFrom в тестовых класса вызываю в основном в методах с анотациями @BeforeClass или @Before
А конфигурации уже в таске gradle
я вот сейчас именно эту штуку активно использую, если речь идет о дефолтных интерфейсах, в которых можно имплементировать только реализацию, без состояния
Речь шла о дефолтных и статических методах в интерфейсах. В девятке еще и приватные методы добавят.
ага, я эт и имел ввиду. Сейчас стабильно в своих автотестах юзаю эти вещи. Очень сильно улучшает фреймворк…
В восьмой джаве очень много фишек, значительно улучшающих дизайн архитектуры.
Это точно
Интересно: а как у Вас описаны классы страниц?
В частности интерес представляет расположение веб-айтемов в классе (атрибуты класса или атрибуты инстанса)
Столкнулся на днях с некоторыми проблемами при определении веб-элементов как атрибутов класса, интересно как подобные проблемы решаются другими людьми.
И реализовывали ли веб-айтемы вида “контейнер”? Т.е. группа элементов связанная логически (input, div с эррор-меседжем, etc), для которой длеается отдельный реюзабельный класс, дабы каждый раз не описывать по 3-5 веб-айтемов на странице.
Примерно так:
class TagLoginPage(WebPage):
web_page_id = (By.ID, 'login')
def __init__(self):
super().__init__()
# Page items
self.email = Input(By.XPATH, '//input[@type="email"]', self)
self.password = Input(By.XPATH, '//input[@type="password"]', self)
self.submit = Button(By.XPATH, '//input[@type="submit"]', self)
def invoke_actions(self):
self.browser_session.open(JSONReader.get_data_from_string("environment_links", "tag", json_string=Vars().env_config))
@allure.step
def login(self):
self.invoke().email.send_keys(Vars().retailer_email)
self.password.send_keys(Vars().retailer_password)
self.submit.click()
Спасибо, полезно. Жаль что я выбрал более заковыристый путь, в угоду красоте и юзабельности “не программистами”
как по мне - самый удобный вариант ,
не лучший пример, но SelenideElement emailInputField= $("#email"); выглядит вполне щикарно и просто
Только работать не будет так как по дефолту идет CSS, а у Вас написан xPath.
вот так будет правильней.
SelenideElement loginField = $(By.xpath("//*[@id='email']"));
Та да, то я затупил, когда писал, у меня так и написано Просто когда копировал с первого примера, то копипастил все что в кавычках к сожалению позже уже поправить не смог, поскольку первое сообщение из темы, сейчас тоже поправить не могу
Что скажите о таком подходе?
Сделайте more DRY and KISS - в текущей реализации на 3 с минусом.
Не могли бы вы привести свой вариант? Интересно, как вы видите вариант на 5.
А вы тесты покажите - будет точка отсчёта.
Там в ветке есть тесты берите “точку отсчёта”. В принципе для PO не нужна логика тестов. Возьмите любую страницу и сделайте PO, а мы посмотрим, поучимся.