0

1つの入力フィールドと送信ボタンのある小さなフォームのページがあります。送信ボタンはAJAXです!クリックすると、入力値がサーバーに送信され、サーバーはそれを受け入れ可能であると検証して新しいページをロードするか、問題を見つけてフィードバックエラーラベルを追加します。

この機能をテストするために、次のセレンコードを作成しました。

WebDriverWait waiter = new WebDriverWait(driver, 20);
WebElement inputField = waiter.until(ExpectedConditions.presenceOfElementLocated(By.id("inputField")));
inputField.sendKeys("Test input");

WebElement submitInput = waiter.until(ExpectedConditions.presenceOfElementLocated(By.id("submitInput")));
submitInput.click();

WebElement feedback = waiter.until(ExpectedConditions.presenceOfElementLocated(By.id("feedback")));
Assert.assertTrue("Feedback not was not empty, but was: " + feedback.getText(), feedback.getText().isEmpty());

問題は、selenium docssubmitInput.click()のようにイベントを介して新しいページが読み込まれると、すぐに完了して戻ることです。したがって、「フィードバック」による取得の呼び出しは正常に機能し、デバッグを通じて、要素が正しく取得されることがわかります。ただし、ページがバックグラウンドで更新されたため、を呼び出すとが表示されます。StaleElementExceptionfeedback.getText()

次の方法なしでテストに合格するにはどうすればよいですか。

  1. Thread.sleep()を追加します。
  2. StaleElementException要素を再フェッチするためのキャッチを追加します。

:上記のテストは、ページが更新されていないために入力が無効な場合に機能しますが、入力が有効な場合には機能しません。

どんな助けでも大歓迎です。

編集:これは、要素が表示されるまでページをポーリングする場合の問題ではなく、submitInput.click()新しいページをロードするか、既存のラベルにテキストを追加するかを検出する場合の問題であることに注意してください。

編集2:問題を説明するために、テストログとサーバーログをインターリーブするタイムログランスルーをコンパイルしました。

16:34:48,589 - TEST   - Navigating to page.
16:34:48,605 - SERVER - Page request started.
16:34:48,631 - SERVER - Page request ended.
16:34:49,050 - TEST   - Typing test input.
16:34:49,456 - TEST   - Clicking button.
16:34:49,519 - SERVER - Received click event.
16:34:49,529 - SERVER - Responding with redirect.
16:34:49,556 - TEST   - Finding feedback by id.
16:34:49,560 - SERVER - Page request started.
16:34:49,581 - SERVER - Page request ended.
16:34:49,775 - TEST   - Test threw exception.

サーバーは、テストから送信されてから0.5秒強でクリックイベントを受信することがわかります。すぐに(この場合は)リダイレクトコマンドで応答します。その間、クリックがAJAXイベントであるため、ドキュメントに従ってテストがバックグラウンドで続行されました。これにより、サーバーが新しいページ要求を受信する0.004秒前に、テストでフィードバックが取得されます(ご覧のとおり)。feedback.getText()解析されてChromeDriverに送信されるまでに、サーバーはすでに新しいページで応答しているため、この関数は古い例外を返します。

重要な点は、を使用してこれをうまく修正できることですThread.sleep(1000)が、これは理想からはほど遠いです。1つは、サーバーがAJAXを解析して結果を返すのに0.5秒以上かかる場合があるためです。これにより、テストが非決定的になります。2つ目は、これらの時間がすぐに積み重なって時間を浪費するためです。数ミリ秒が必要でした。

4

4 に答える 4

2

私は実際にこの問題を解決するために他の方法で行動します。StaleElementExceptionxpathsの代わりに+++流暢な待機+++cssセレクターを克服するためにいくつかのjsを使用します 。1)流暢な待機方法。実際の(配置された)Web要素を返します。

public WebElement fluentWait(final By locator){
    Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
            .withTimeout(30, TimeUnit.SECONDS)
            .pollingEvery(5, TimeUnit.SECONDS)
            .ignoring(NoSuchElementException.class);

    WebElement foo = wait.until(
        new Function<WebDriver, WebElement>() {
            public WebElement apply(WebDriver driver) {
                return driver.findElement(locator);
            }
        }
    );
    return  foo;
};

あなたがここここに着くことができる流暢な待機について

2)String cssSelectorInputField = "[id ='inputField']" String cssSelectorSubmitInput = "[id ='submitInput']" String cssSelectorFeedback = "[id ='feedback']"

3)ここで、いくつかのjsMethodsテンプレートを提供します(自動化に使用するので、prollyが役立ちます)

public String jsGetText(String cssSelector){

  JavascriptExecutor js = (JavascriptExecutor) driver;
        StringBuilder stringBuilder = new StringBuilder();

stringBuilder.append("var x = $(\""+cssSelector+"\");");
        stringBuilder.append("return x.text().toString();")       ;


       String res= (String) js.executeScript(stringBuilder.toString());
return res;
}

public String jsGetColor(String css){

        JavascriptExecutor js = (JavascriptExecutor) driver;
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("var x=$(\'"+css+"\');");
        stringBuilder.append("return x.css('color')");
        String res= (String) js.executeScript(stringBuilder.toString());
        return res;

    }

public void jsClickOnElement(String css){

JavascriptExecutor js = (JavascriptExecutor) driver;
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("var x = $(\'"+css+"\');");
        stringBuilder.append("x.click();");
        js.executeScript(stringBuilder.toString());

}

4)したがって、最終ステップは次のようになります。

driver.findElement(By.cssSelector(cssSelectorInputField)).sendKeys("Test input");
driver.findElement(By.cssSelector(cssSelectorSubmitInput)).click();
//or jsClickOnElement(cssSelectorSubmitInput);
WebElement feedBack =fluentWait(cssSelectorFeedback);
String textForVerifying=feedBack.getText().trim();
//or String textForVerifying= jsGetText(cssSelectorFeedback);
Assert.assertTrue(textForVerifying.equals("..blablablab..."));

これが今あなたのために働くことを願っています)

于 2012-09-27T11:12:23.830 に答える
0

次のような方法を忘れないでください

input.findElements(By.xpath("//xpath")).size() > 0


driver.findElement(By.xpath("//xpath")).isDisplayed()

public bool IsElementPresent(By selector)
{
    return driver.FindElements(selector).Any();
}

ページ上の要素の存在を確認するため。彼らはまた非常に役立つことができます。ただし、見つけたロケーターに注意してください(Firebugで、すべての要素が正しく配置されていることを確認してください)

于 2012-09-27T15:31:06.377 に答える
0

これをJavaでどのように実行できるかわからないし、シナリオを正しく理解したかどうかもわかりませんが、試してみます。そのRubyで...

 @wait.until { @driver.find_element(:id,"feedback") }
 assert "true","Feedback was not empty but was #{@driver.find_element(:id,\"feedback\").text}"

私が基本的にやろうとしているのは、アサーションの上の行で、見つかった/存在する場所にあるWeb要素「フィードバック」を使用しないことです。代わりに、Webドライバーに要素を再度検索させてから、アクション(テキストの取得)を実行しました。

編集


@wait.until { alert_text = @driver.find_element(:id,"feedback").text }
assert_match "expected text","#{alert_text}","Feedback is not empty and expected"
于 2012-09-28T02:44:53.683 に答える
0

同様の問題(1-AJAX制御のページ変更の検出と2-AJAX制御要素の変更の検出の2つの異なる問題)を抱えている人のための私の解決策は、最終的には

ステップ1:jQueryを介してページの変更を追跡するいくつかの関数を作成します(jQueryはすべてのページで使用されるため、スクリプトは正常に実行されます):

private void updateLastChange()
{
    driver.executeScript("$('html').data('CHANGED_PAGE', false)");
}

public boolean pageChanged()
{
    Object script = driver.executeScript("return $('html').data('CHANGED_PAGE');");
    Boolean changed = (Boolean)script;
    if(changed == null)
    {
        updateLastChange();
        return true;
    }
    return false;
}

次に、電話pageChanged()してページの変更を検出できます。

ステップ2:を実装する匿名クラスを作成しますExpectedCondition<Boolean>

フィードバックがいつ準備できるかに関して決定論的ではないという問題があった上記の例を使用して、質問のコードから下の2行を次のように変更しました。

WebDriverWait waiter = new WebDriverWait(driver, 5, 200);
waiter.ignoring(AssertionFailedError.class);
waiter.ignoring(StaleElementReferenceException.class);
waiter.until(new ExpectedCondition<Boolean>()
{
    public Boolean apply(WebDriver input)
    {
        WebElement feedback = input.findElement(By.id("feedback"));
        Assert.assertTrue("Feedback not was not empty, but was: " + feedback.getText(), feedback.getText().isEmpty());            
        return true;
    }
});

このようにして、アサーション例外と古い要素の例外を無視して、200ミリ秒ごとに5秒間ページをポーリングします。それが通過する唯一の方法はreturn true;、5秒がなくなる前にラインに到達した場合です。私は現在、これをすっきりさせる方法を考えていますが、それはうまく機能し、単に寝るよりも速く、ポーリングのために(可能であれば)早く成功します。

于 2012-10-02T15:37:03.737 に答える