2012-10-05 12 views
5

Jeśli zaznaczysz nieobecnych elementy z następującego kodu:Selen 2 - Jak sprawdzić, czy element nie występuje podczas niejawnego oczekiwania?

// ... 
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); 
try { 
    driver.findElement(By.cssSelector("td.name")); 
} catch (NoSuchElementException e) { 

    // here you go, element not found 

} 

uzyskać prawo wynik, ale czas działania jest zawsze 30 sekund, ze względu na findElement metoda blokowania na niejawny czekać.

Czy istnieje sposób na uniknięcie tego zachowania, przy jednoczesnym zachowaniu domyślnego oczekiwania w miejscu?

<EDIT> testy będą generowane przez Selenium IDE przez nie-programistów, więc potrzebne jest rozwiązanie, które utrzymuje swoją pracę tak proste, jak to możliwe (to utrzymanie czeka niejawna!). </EDIT>

Dzięki

Marco

+0

Nie można utworzyć metody centralnej, która ustawia niejawne oczekiwanie na coś małego, a następnie resetuje ją ponownie do 30 sekund? – Arran

+0

Byłbym skłonny całkowicie zrzucić implicity, chociaż używam szablonu do konwersji przypadków testowych napisanych z Selenium IDE i miałem nadzieję, że zmiany w kodzie będą minimalne. –

Odpowiedz

2

Możesz być w stanie to zrobić z selektorów XPath. Znajdź element tuż przed nim, o którym wiesz, że powinien tam być, a następnie użyj "follow-sibling", aby uzyskać następny element. Coś jak:

//td.previous/following-sibling::td 

Następnie sprawdź, czy nie zwrócił "imię" jeden. Oczywiście zadziałałoby to tylko wtedy, gdyby istniał inny element "td".

Osobiście miałbym ochotę rzucić ukryte oczekiwania i po prostu korzystać z czekania, gdy są wymagane.

private WebElement cssWait(final String css) 
{ 
    return new WebDriverWait(driver, 30).until(new ExpectedCondition<WebElement>() 
    { 
     @Override 
     public WebElement apply(WebDriver d) 
     { 
      return d.findElement(By.cssSelector(css)); 
     } 
    }); 
} 
+0

+1 za sprytne rozwiązanie. Niestety ... patrz edytuj –

2

Zamiast ustawiać limity czasu używam fluentWait, które zostały wprowadzone w 2.25.

public void waitForElement(WebDriver driver, final String xpath) 
{ 
//Set up fluentWait to wait for 35 seconds polling every 1 
Wait<WebDriver> fluentWait = new FluentWait<WebDriver>(driver) 
    .withTimeout(35, TimeUnit.SECONDS) 
    .pollingEvery(1, TimeUnit.SECONDS) 
    .ignoring(NoSuchElementException.class); 

WebElement element; 

//Look for element, if not found start fluentWait 
try 
{ 
    element = driver.findElement(By.xpath(xpath)); 
} 
catch (WebDriverException e) 
{ 
    logger.info("[getElementByXpath] Element not initially found. Starting fluentWait ["+xpath+"]"); 

    try 
    { 
     element = fluentWait.until(new Function<WebDriver, WebElement>() { 
      public WebElement apply(WebDriver d) { 

       return d.findElement(By.xpath(xpath)); 
      } 
     }); 
    } 
    catch (WebDriverException f) 
    { 
     logger.info("[getElementByXpath] FluentWait findElement threw exception:\n\n" + f +"\n\n"); 

     throw new WebDriverException("Unable to find element ["+xpath+"]"); 
    } 
} 

//Once we've found the element wait for element to become visible 
fluentWait.until(ExpectedConditions.visibilityOf(element)); 
} 

Jeśli było konwertować swoje metody do czegoś takiego, byłbyś w stanie usunąć driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); pozwalając na „nie” znajdź element natychmiast.

Mam nadzieję, że to pomoże!

+0

Tak właśnie normalnie chodzę, dziękuję. Niestety ... zobacz edycję. –

3

Powyższe metody czekają na podany czas, nawet jeśli element nie jest już obecny. Napisałem własne metody oczekiwania, aż element będzie widoczny i nie będzie obecny. Pracują dla mnie. Oto one:

public void waitUntilElementExists(By by, int waitSeconds, 
     int noOfRetries) { 
    getDriver().manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS); 
    boolean foundElement = false; 
    for (int i = 0; i < noOfRetries; i++) 
     try { 
      getDriver().findElement(by); 
      foundElement = true; 
      break; 
     } catch (Exception e) { 
     } 
    assertTrue("The searched element was not found after " + noOfRetries * waitSeconds + " seconds!", foundElement); 
} 

public void waitUntilElementDoesntExist(By by, int waitSeconds, 
     int noOfRetries) { 
    getDriver().manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS); 
    boolean elementDisappeared = false; 
    for (int i = 0; i < noOfRetries; i++) 
     try { 
      getDriver().findElement(by); 
      waitABit(1000 * waitSeconds); 
     } catch (Exception e) { 
      elementDisappeared = true; 
      break; 
     } 
    assertTrue("The searched element did not disappear after " + noOfRetries * waitSeconds + " seconds!", elementDisappeared); 
} 
0

Trzeba funkcję jak ten, który używa findElements, nie findElement:

public static ExpectedCondition<Boolean> elementCountIs(final By sel, final int count) { 
    return new ExpectedCondition<Boolean>() { 
     public Boolean apply(WebDriver driver) { 
      return driver.findElements(sel).size() == count; 
     } 
    }; 
} 

Następnie można skonfigurować FluentWait obiekt opisany przez Falkenfighter oraz:

fluentWait.until(elementCountIs(By.cssSelector("td.name"), 0); 
+0

Przepraszamy, teraz zrozumiałem, że to jest nieprawidłowe. Niejawne oczekiwania odnoszą się równie dobrze do findElements, co findElement, co jest bardzo denerwujące. –

0

Będziesz musiał tymczasowo zaktualizować ImplicitWait i zresetować go po zakończeniu.

W ten sposób mamy do czynienia z tą sytuacją - zapisz bieżącą wartość domyślną, tymczasowo zaktualizuj ImplicitWait, a następnie powróć do domyślnej.
ta opiera się sugestia Mozilli, czyli w jaki sposób radzą sobie tę sytuację, w której spodziewasz się czegoś nie być obecne: https://blog.mozilla.org/webqa/2012/07/12/webdrivers-implicit-wait-and-deleting-elements/

public bool ElementExists(By by, int waitMilliseconds) 
{ 
     var defaultWebDriverTimeout = 30000;// Get default timeout that you're using 
     WebDriver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(waitMilliseconds)); 

     try 
     { 
      WebDriver.FindElement(by); //Note could be FindElements instead, but this is quicker 
      return true; 
     } 
     catch (NoSuchElementException) 
     { 
      return false; 
     } 
     finally 
     { 
      WebDriver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(defaultWebDriverTimeout)); 
     } 
} 
0

Nie, nie można. implicit czas oczekiwania będzie miał pierwszeństwo przed jawnymi oczekiwaniami. Jeśli twój czas implicit wynosi 30 s, każde znalezione działanie będzie miało co najmniej 30 sekund na wypadek, gdyby element nie był obecny. To, co możesz zrobić, to manipulować czasem oczekiwania w strukturze, ale nie wiesz, jak to jest z IDE, nigdy z nim nie pracowałem.

Utworzono niestandardową metodę, która zwraca boolean z wynikiem. Dane wejściowe są dowolne Według lokalizatora obsługiwane przez WebDriver (CSS, xpath, itp.). Lub możesz modyfikować, jak chcesz.

Pomogło uczynić mój kod czystszym i szybszym. Mam nadzieję, że pomoże to także innym ludziom.

Domyślny pooling to 500 milisekund, ale można go zmienić na obiekcie wait.

public boolean isElementNotPresent(final By locator) { 
     boolean result = false; 
     // use your custom timeout here 
     long timeout = ConfigurationProvider.getWebDriverWaitTimeout(); 

     // log4j used 
     msg = "isElementNotPresent: " + locator; 
     LOG.info(msg); 

     Wait<WebDriver> wait = new FluentWait<WebDriver>(
       getDriver()).withTimeout(timeout, TimeUnit.SECONDS); 

     try { 
      result = wait.until(new Function<WebDriver, Boolean>() { 
       @Override 
       public Boolean apply(WebDriver driver) { 
        return driver.findElements(locator).size() == 0; 
       } 
      }); 
     } catch (TimeoutException e) { 
      msg = String.format("Element remained visible after %.2f seconds", 
      ((float) timeout/1000)); 
      LOG.debug(msg); 
     } catch (Exception e) { 
      msg = "Exception at isElementNotPresent()\n" + e.getMessage(); 
      // I use jUnit to fail my test 
      Assert.fail(msg); 
     } 

     return result; 
    };