Обработка сред с похожей логикой страницы, но разными локаторами

У меня есть набор тестов, который в настоящее время работает в разных средах разработки. Недавно приложение было полностью переписано и развернуто в новой среде.

Приложение выглядит и действует практически одинаково. Логика страницы более или менее одинакова. Большая разница в том, что переписывание HTML сделало мои локаторы бесполезными. Я не уверен, как работать с локаторами для этой новой среды, в то же время придерживаясь объектной модели страницы.

В объектной модели страницы указано, что вся логика страницы должна храниться в соответствующем классе объекта страницы. Я предполагаю, что это включает в себя и локаторы.

Следование этой стратегии оставило бы меня с раздутым классом объектов страницы, полным повторяющихся локаторов. Существуют ли какие-либо рекомендуемые передовые методы или чистые решения для борьбы с этой проблемой?

Возможные решения, о которых я могу думать, следующие:

  1. Создайте отдельные файлы локаторов для каждой среды и удалите их из класса объектов страницы.
  2. Создать дубликаты или локаторы с аналогичными именами в текущем классе объектов страницы.
  3. Создайте отдельные объекты страницы для новой среды

Кто-нибудь может прокомментировать, звучат ли эти решения нормально? Или предложите альтернативные варианты?


person Cathal    schedule 16.12.2016    source источник
comment
Используете ли вы аннотацию FindBy в своих классах PageObject? Или вы используете findElement из кода?   -  person Grasshopper    schedule 16.12.2016
comment
Я использую аннотации @FindBy   -  person Cathal    schedule 16.12.2016


Ответы (2)


Определенно переместил бы локаторы за пределы класса pageobject в два разных класса: один для старых локаторов и один для новых локаторов. Используйте общедоступную статическую конечную строку для каждого локатора. Проблема, с которой вы столкнетесь, заключается в том, что значения аннотации Java требуют постоянного выражения, поэтому вы не можете использовать метод для отправки другого локатора в FindBy. Но вы можете использовать тернарный оператор для создания константного выражения.

Ниже я добавил код, который на основе глобального флага нажимает на другую кнопку из одного веб-элемента, но локатор изменяется выражением для значения «использование» аннотации FindBy. Вы можете установить значение глобального флага при запуске, когда вы инициализируете драйвер из файла свойств.

Это то, что вам нужно будет включить в FindBy и локаторы, отправленные в findElement() - using= GlobalFlag.devEnv ? NewLocators.newLocxpath : OldLocators.newLocxpath. Это будет больно копировать вставить везде.

Вы можете попробовать код, так как веб-сайт общедоступен.

class CartConstant {
    //Old locators
    public static final String cartxpath = "//span[.='Cart']";
}

class AccountConstant {
    //New locators
    public static final String accxpath = "//span[.='Account']";
}

class GlobalFlag {
    //Initialize this at the start
    public static final boolean devEnv = true;
}

public class ChangeAnnotation {
    //Change this in the code to include the choice
    @FindBy(how=How.XPATH, using=GlobalFlag.devEnv ? AccountConstant.accxpath : CartConstant.cartxpath)
    private WebElement butt;

    @Test
    public void demoSQA() throws InterruptedException {

        System.setProperty("webdriver.chrome.driver", "E:/Software Testing/Selenium/Jars/chromedriver.exe");
        ChromeOptions chop =  new ChromeOptions();
        chop.addArguments("test-type");
        chop.addArguments("start-maximized");
        WebDriver driver = new ChromeDriver(chop);

        driver.get("http://store.demoqa.com/products-page/product-category/imacs/");        
        Thread.sleep(3000);     
        PageFactory.initElements(driver, this);     
        butt.click();               
    }
}
person Grasshopper    schedule 16.12.2016
comment
Эй, Кузнечик, спасибо за предложение. Я думаю, что вы на правильном пути со своим предложением. Оптимальное решение, вероятно, включает создание собственного ElementLoactorFactory, но, к сожалению, я не совсем понимаю, как это сделать. В итоге я полностью переписал объект страницы, потому что разница в логике страницы была больше, чем я думал изначально. Однако теперь у меня осталось достаточное количество дублированного кода. - person Cathal; 20.12.2016
comment
Написание собственного пользовательского кода не является серьезной задачей, так как это будет просто расширение классов по умолчанию в коде Selenium. Я думаю, вам также нужно будет добавить новый параметр в свой собственный класс FindBy, расширяющий существующий. В противном случае это испортит существующий «использующий». Извлеките свой дублированный код в базовый класс или какую-либо другую стратегию, почти наверняка он придет и укусит вас на более позднем этапе с дублированием, если у вас есть растущий проект. - person Grasshopper; 20.12.2016

Вот как мне удалось это преодолеть.

Я хранил разные файлы свойств элементов для разных сред.

Допустим, среда A, B. В моем проекте я храню два файла свойств с именами Elements_A.properties и Elements_B.properties. Эти файлы свойств содержат все элементы страницы. Если один элемент отличается от другого, это не будет проблемой, поскольку при запуске сценария на основе среды вы можете сослаться на соответствующий файл свойств в сценарии.

Допустим, в A и B на домашней странице есть текстовое поле с разными локаторами.

Таким образом, в файле свойств A мы можем упомянуть элемент как HomePage_Name_TextBox = id_NameInA «id_NameInA» — это значение локатора, а «HomePage_Name_TextBox» — это строка, которую вы собираетесь использовать для ссылки на этот конкретный элемент.

Как и в файле свойств A, мы можем упомянуть тот же элемент, что и HomePage_Name_TextBox = id_NameInB «id_NameInB» — это значение локатора, а «HomePage_Name_TextBox» — это строка, которую вы собираетесь использовать для ссылки на этот конкретный элемент.

Вы можете заметить, что обоим элементам присвоено одно и то же имя (HomePage_Name_TextBox), а значения локатора различаются.

В каждом классе страницы я объявил карту, и теперь у вас есть несколько вариантов, чтобы решить, как вы собираетесь инициализировать элементы для своей страницы.

public class HomePage {

   Map<String, String> elementsMap = new HashMap<String, String>();

   //Option 1
   public HomePage(Map<String, String> elementMapObj) {
      elementsMap = elementMapObj;
   }

   //Option 2
   public HomePage() {
        Properties              prop        = new Properties();
        FileReader              reader;
        HashMap<String, String> propertyMap = new HashMap<String, String>();

        try {
            reader = new FileReader(new File("CommonConfig.properties"));
            prop.load(reader);
            for (String key : prop.stringPropertyNames()) 
            {
                String value        = prop.getProperty(key);
                propertyMap.put(key, value);
            }
        } catch (Exception e) {
            //System.out.println(e.toString());
        }

        try {
            reader = new FileReader(new File(propertyMap.get("ElementPropFilePath")));
            prop.load(reader);
            for (String key : prop.stringPropertyNames()) 
            {
                String value        = prop.getProperty(key);
                elementsMap.put(key, value);
            }
        } catch (Exception e) {
             //System.out.println(e.toString());
        }

   }
}
  1. Вы можете объявить параметризованный конструктор и передать объект карты, где у вас есть все элементы, прочитанные перед инициализацией объекта HomePage (в вашем основном классе вы можете прочитать файл свойств соответствующего элемента и передать карту, содержащую все элементы, конструктору Home Страница)
  2. ИЛИ вы можете прочитать общий файл свойств в самом конструкторе HomePage, где вы можете указать путь к файлу свойств Elements. пример: CommonConfig.properties этот файл может содержать все детали конфигурации, такие как среда, в которой вы запускаете сценарии, и общие пути к файлам для чтения (например, путь к файлу элемента), и этот файл будет считан внутри файла конфигурации конструктора. будет выглядеть так
    ElementPropFilePath = Resources/Elements_A.properties

Если вы хотите запускать сценарии в A, вы можете изменить «ElementPropFilePath» в общем файле свойств на «Resources/Elements_A.properties» перед запуском. Если вы хотите запускать сценарии в B, вы можете перед запуском изменить «ElementPropFilePath» в общем файле свойств на «Resources/Elements_B.properties» (это расположение файла на машине).

Проще говоря, если вы поддерживаете файлы свойств, содержащие все элементы для каждой среды, и предоставляете сведения об этом свойстве и заполняете elementMap, который у вас есть внутри каждого класса страницы, тогда вы сможете ссылаться на этот элемент с помощью общей строки, которую вы использовали для обе среды (в данном примере это HomePage_Name_TextBox)

person Sanoj Indrasinghe    schedule 19.12.2016