Отследить изменение текста после выполнения AJAX

webdriver
junit
ajax
Теги: #<Tag:0x00007fedc0068910> #<Tag:0x00007fedc0068528> #<Tag:0x00007fedc00682a8>

(Kosmos) #1

Добрый день.

Как можно отследить изменение текста на странице с помощью Selenium + Java после выполнения AJAX?

Т.е. например был локатор с текстом "Text 1" -> далее AJAX -> далее в этом локаторе "Text 2".

Спасибо


(Sergey Korol) #2

Кастомные ExpectedConditions - стандартная практика для таких случаев. Вам нужен вариант - обратный textToBePresentInElementLocated:

public void textChanged(final By locator) {
    final String currentText = getText(locator);
    wait.until((WebDriver driver) -> !getText(locator).equals(currentText));
}

Note: getText - обертка над страндартным API.
Если нужно возвращать true / false, хэндлите exception:

    public boolean textChanged(final By locator) {
	final String currentText = getText(locator);
	return Try.run(() -> wait.until((WebDriver driver) -> !getText(locator).equals(currentText)))
			.isSuccess();
    }

Note: Try - functional style замена стандартному try / catch из библиотеки javaslang.


(Kosmos) #3

круто! Спасибо ))


(Kosmos) #4

компилятор ругается на getText() - не известная ф-ция. Что нужно подключить?


(Sergey Korol) #5

driver.findElement(locator).getText();

(asolntsev) #6

Хм... Неужели никто не скажет? :slightly_smiling:
Selenide же идеально для этого подходит!

$("div").shouldHave(text("Text 1"));
$("button").click();
$("div").shouldHave(text("Text 2"));

И всё! Вот так просто. Никаких тебе ожиланий, никаких лямбд, никаких слипов и циклов.


(Sergey Korol) #7

Исходя из предложенной формулировки, речь шла о динамическом изменении текста. Ни о каких промежуточных действиях (по типу клика) никто ничего не говорил. Банальный пример - изменение статуса загрузки файла: uploading -> validating -> done. Напишите пример на Selenide, как вы будете отслеживать текст динамически изменяемой лейблы без ожиданий, лямбд и прочего. Или сам факт изменения текста, когда не нужно ничего асертить, а лишь понять, что можно продолжать дальнейшее взаимодействие со страницей.


(asolntsev) #8

@ArtOfLife Ну нет, там было очень чётко сказано, что текст известен:

был локатор с текстом "Text 1" -> далее AJAX -> далее в этом локаторе "Text 2".

Впрочем, неважно. Без действий его можно точно так же проверить:

$("div").shouldHave(text("Text 1"));
$("div").shouldHave(text("Text 2"));

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

А если даже точные тексты неизвестны и хочется проверить только сам факт изменения текста, можно, например, так:

String initialText = $("div").text();
$("div").shouldNotHave(text(initialText));

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


(Sergey Korol) #9

Отследить != верифицировать. В данном условии нам не нужно проверять конкретный текст. Нам нужно узнать, а не изменился ли исходный.

Последний пример ближе к истине. Но вот если мне нужно будет задать определенный интервал ожидания, да еще и хэндлить потенциальный exception, ваш код обрастет все теми же вейтами и try / catch, разве нет?


(asolntsev) #10

Да, пожалуй что обрастёт.
Но просто не надо этого делать, во вселенной и без этого полно зла.


(Kosmos) #11

Зачем в строке

wait.until((WebDriver driver) -> !getText(locator).equals(currentText));

указывать "(WebDriver driver) ->"? Почему нельзя просто написать

wait.until(!getText(locator).equals(currentText));

Проверял и знаю, что будет ошибка. Не понятно, почему? Это стандарт такой для всех кастомных wait'оров?

В случае, например, если необходимо дождаться, пока bar не будет больше 5, то код будет выглядеть так:

wait.until((WebDriver driver) -> bar>5);

?


(Sergey Korol) #12

Потому что результат вашего условия - boolean, а метод until принимает один из следующих функциональных интерфейсов:

Function тут само собой не подойдет, ибо она должна возвращать WebElement, т.е. остается Predicate, который на вход принимает драйвер, а возвращает boolean. Технически вам никто не запрещает игнорировать input аргументы. Тут важно, чтобы лямбда соответствовала определению абстрактного метода требуемого функционального интерфейса.

Подробнее о механике работы лямбд, и их взаимосвязи с функциональными интерфейсами, можете посмотреть тут:

П.С. Хотя, в селениуме заложены гуавовские интерфейсы, суть от этого не меняется.