Регрессионное тестирование Selenium на шаблонах страниц, которые могут быть включены или не включены

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

Текущий локатор и WebElement:

@FindBy(className = "add-to-cart-btn")
    WebElementFacade addToCartBtn;

Новый локатор в новом шаблоне:

className = "quickOrderSubmit"

person charliebigpotato    schedule 19.02.2018    source источник
comment
Добро пожаловать в сообщество Stack Overflow. Пожалуйста, найдите время и завершите Приветственный тур и прочитайте Как задать хороший вопрос? руководство.   -  person Krzysztof Łach    schedule 19.02.2018
comment
Это переключение делается для всего сайта одновременно? Или на одних страницах могут быть старые локаторы, а на других новые?   -  person Grasshopper    schedule 19.02.2018
comment
@Grasshopper переключение выполняется на уровне страницы.   -  person charliebigpotato    schedule 19.02.2018
comment
@charliebigpotato Есть способ использовать троичное выражение в аннотации findby — Flag.devEnv? Новые локаторы : Старые локаторы. Проблема в том, что flag.devenv должен быть константой времени компиляции и поэтому не может быть выведен из заголовка. Это легко реализовать для сайта в целом или, может быть, для нескольких страниц. Кстати, как вы получаете доступ к этому заголовку?   -  person Grasshopper    schedule 19.02.2018
comment
@Grasshopper извините за ошибку, это идентификатор в теле, а не в заголовке.   -  person charliebigpotato    schedule 19.02.2018
comment
Существуют ли оба локатора при включении страницы? например Если страница включена, существуют ли оба элемента, но виден только один и т. д.?   -  person JeffC    schedule 19.02.2018
comment
@JeffC Нет, только новые локаторы будут существовать, если будет включена новая страница, а старые будут существовать при выключении.   -  person charliebigpotato    schedule 20.02.2018


Ответы (1)


Нашел способ сохранить старый и новый локаторы в одном месте, используя собственный класс ElementLocatorFactory, ElementLocator and Annotations. Они легко впишутся в объектную модель страницы. Он использует комбинацию полей using и locator value (id,class,css,xpath etc) аннотации FindBy. Это хак использования FindBy. Новая аннотация была бы лучше, но это слишком много работы.

Это будет интегрировано в метод initElements PageFactory с помощью приведенного ниже кода в конструкторе класса pageobject.

OldNewElementLocatorFactory onelf = new OldNewElementLocatorFactory(driver);
PageFactory.initElements(onelf, this);

Применение -

  • Та же стратегия поиска для старого и нового. Например, изменился идентификатор.

@FindBy(using="id@@id", id="newid@@oldid") или @FindBy(using="name@@name", name="newname@@oldname")

использующая часть — первая часть перед разделителем — это стратегия локатора для нового локатора, а вторая часть — для старого локатора.

часть идентификатора. Первая часть представляет собой значение для новой стратегии поиска, а вторая — для старой.

  • Разные стратегии поиска для старого и нового. Например, идентификатор теперь изменился на имя.

@FindBy(using="name@@id", name="newname", id="oldid") или @FindBy(using="xpath@@css", xpath="//div /input[@id='comboid2']", css="div input[id='comboid2']")

используя часть — то же, что и выше.

часть имени — значение локатора для новой стратегии имен.

часть идентификатора — значение локатора для старой стратегии идентификатора.

  • То же самое относится к FindBy, используемому вместе с переменной типа списка

@FindBy(using="class@@tag", className="newClass", tagName="input") входные данные частного списка;

@FindBy(using="class@@class", className="newClass@@oldClass") private List sameInputs;

  • Разрешает стандартный формат использования аннотации FindBy. Никаких изменений не требуется. @FindBy(how=How.ID, using="origid") или @FindBy(id="origid")

ElementLocatorFactory — поля isNew and oldNewChecked управляют вопросом, используются ли новые или старые локаторы. Они относятся к этому классу, поскольку локаторы могут различаться на уровне страницы. Если вы не используете Java8, вам нужно будет изменить метод verifyPageAge(). Позаботьтесь о том, чтобы поиск старого нового переключателя локатора выполнялся только один раз.

public class OldNewElementLocatorFactory implements ElementLocatorFactory {

    private final SearchContext searchContext;

    private boolean isNew = false;
    private boolean oldNewCecked = false;

    public OldNewElementLocatorFactory(SearchContext searchContext) {
        this.searchContext = searchContext;
    }

    public ElementLocator createLocator(Field field) {
        OldNewElementLocator elemLoc = new OldNewElementLocator(searchContext, field);
        elemLoc.setFactory(this);
        return elemLoc;
    }

    public void verifyPageAge(Supplier<Boolean> newLoc) {
        if (!oldNewCecked) {
            oldNewCecked=true;
            if (newLoc.get())
                isNew=true;
        }
    }

    public boolean isNew() {
        return isNew;
    }

    public void setNew(boolean isNew) {
        this.isNew = isNew;
    }

    public boolean isOldNewCecked() {
        return oldNewCecked;
    }

    public void setOldNewCecked(boolean oldNewCecked) {
        this.oldNewCecked = oldNewCecked;
    }
}

ElementLocator — обрабатывает поиск нового старого переключателя локатора в методе verifyNewLocators(). Вам нужно будет изменить этот метод для поиска вашего флага. Я предположил любой элемент с идентификатором newloc. Если не на Java8, вам нужно будет изменить метод createOldNewBy().

public class OldNewElementLocator implements ElementLocator {

    private final SearchContext searchContext;
    private final boolean shouldCache;
    private By by = null;
    private WebElement cachedElement;
    private List<WebElement> cachedElementList;
    private OldNewAnnotations annotations;


    private OldNewElementLocatorFactory factory;

    public OldNewElementLocator(SearchContext searchContext, Field field) {
        this(searchContext, new OldNewAnnotations(field));
    }

    public OldNewElementLocator(SearchContext searchContext, OldNewAnnotations annotations) {
        this.searchContext = searchContext;
        this.shouldCache = annotations.isLookupCached();
        this.annotations = annotations;

        this.by = annotations.buildBy();
    }

    public OldNewElementLocatorFactory getFactory() {
        return factory;
    }

    public void setFactory(OldNewElementLocatorFactory factory) {
        this.factory = factory;
    }

    protected boolean verifyNewLocators() {
        return (searchContext.findElements(By.id("newloc")).size() == 1) ? true : false;
    }

    protected void createOldNewBy() {
        factory.verifyPageAge(this::verifyNewLocators);
        if(by==null) {
            by = annotations.buildOldNewBy(factory.isNew());
        }
        System.out.println(by.getClass().getSimpleName());
    }

    public WebElement findElement() {
        if (cachedElement != null && shouldCache) {
            return cachedElement;
        }

        createOldNewBy();

        WebElement element = searchContext.findElement(by);
        if (shouldCache) {
            cachedElement = element;
        }

        return element;
    }

    public List<WebElement> findElements() {
        if (cachedElementList != null && shouldCache) {
            return cachedElementList;
        }

        createOldNewBy();

        List<WebElement> elements = searchContext.findElements(by);
        if (shouldCache) {
            cachedElementList = elements;
        }

        return elements;
    }

    @Override
    public String toString() {
        return this.getClass().getSimpleName() + " '" + by + "'";
    }
}

Annotations — управляет логикой синтаксического анализа для создания объектов By. Также содержит жестко закодированный разделитель, который я использовал — @@. Измените его или создайте для этого статическое поле.

public class OldNewAnnotations extends Annotations {

    public OldNewAnnotations(Field field) {
        super(field);
    }

    public By buildBy() {
        if (assertNewOldFind())
            return null;
        return super.buildBy();
    }

    public By buildOldNewBy(boolean isNew) {

        if (!assertNewOldFind())
            throw new IllegalArgumentException(
                    "If you set the 'using' property, you cannot also set 'how'");

        FindBy findBy = getField().getAnnotation(FindBy.class);

        String[] using = findBy.using().split("@@");
        String strategy = isNew ? using[0] : using[1];

        switch (strategy) {

        case "id":
            return By.id(getStrategyLocator(findBy.id(), isNew));

        case "name":
            return By.name(getStrategyLocator(findBy.name(), isNew));

        case "class":
            return By.className(getStrategyLocator(findBy.className(), isNew));

        case "css":
            return By.cssSelector(getStrategyLocator(findBy.css(), isNew));

        case "xpath":
            return By.xpath(getStrategyLocator(findBy.xpath(), isNew));

        case "link":
            return By.linkText(getStrategyLocator(findBy.linkText(), isNew));

        case "partiallink":
            return By.partialLinkText(getStrategyLocator(
                    findBy.partialLinkText(), isNew));

        case "tag":
            return By.tagName(getStrategyLocator(findBy.tagName(), isNew));
        }
        return null;
    }

    private String getStrategyLocator(String value, boolean isNew) {

        if (value.contains("@@")) {
            String[] val = value.split("@@");
            return isNew ? val[0] : val[1];
        }
        return value;
    }

    protected boolean assertNewOldFind() {

        FindBys findBys = getField().getAnnotation(FindBys.class);
        if (findBys != null) return false;

        FindAll findAll = getField().getAnnotation(FindAll.class);
        if (findAll != null) return false;

        FindBy findBy = getField().getAnnotation(FindBy.class);

        if (findBy.how() == How.UNSET && !"".equals(findBy.using()))
            return true;
        return false;
    }

}

Минусы -

  1. Проверка ошибок при использовании нового формата аннотаций невелика. Вы можете посмотреть, как сделать его прочным.

  2. Он не обрабатывает аннотации FindBys и FindAll вместе со старым и новым локатором. Это могло бы быть легко, но исходный код Selenium имеет некоторые частные методы, которые затрудняют использование переопределения путем наследования. Вы можете посмотреть на другой способ решить эту проблему. Хотя вряд ли когда-нибудь увидишь, что эти аннотации используются.

person Grasshopper    schedule 20.02.2018