[英]Selenium and Parallelized JUnit - WebDriver instances
所以,基本上我正在嘗試使用JUnit實現並行運行的Selenium測試。
為此我找到了這個JUnit跑步者 。 它工作得很好,我很喜歡它。
但是,我遇到了有關WebDriver實例處理的問題。
在執行@Test
方法之前,應為每個類創建一個WebDriver元素。
從邏輯上講,我可以使用類構造函數。 實際上這是我的測試的要求,因為我需要使用@Parameters
以便我可以相應地創建WebDriver實例(Chrome,FF,IE ......)。
問題是我希望在完成一個類之后清除WebDriver實例( driver.quit()
),而不是在每個@Test
方法完成之后清除它。 但是我不能使用@AfterClass
因為我無法使WebDriver成為靜態成員,因為每個類實例都必須使用它自己(否則測試會嘗試在同一個瀏覽器中運行)。
我在這里找到了Mrunal Gosar的可能建議。 根據他的建議,我已經將WebDriver改為static ThreadLocal<WebDriver>
,然后我在每個構造函數中創建它的實例
// in the classes constructor
driver = new ThreadLocal<WebDriver>() {
@Override
protected WebDriver initialValue() {
return new FirefoxDriver(); /
}
};
有人說我替換了每個driver.whatever
driver.get().whatever
使用driver.get().whatever
調用, driver.get().whatever
我的代碼是driver.get().whatever
。
現在,為了解決這個問題的最終目的,我還寫了一個@AfterClass
方法,它將調用driver.get().quit();
現在由編譯器接受,因為變量是靜態的。
然而,測試這會導致意外行為。 我有一個Selenium Grid設置,在遠程計算機上運行2個節點。 我之前已按預期運行此設置,但現在瀏覽器全部被垃圾郵件發送並且測試失敗。 (雖然應該運行2個瀏覽器而不是8個以上)
我鏈接的線程暗示這個解決方案有人評論說,如果已經使用像JUnit這樣的框架,手動處理線程可能是個壞主意。
做到這一點的正確設計是什么?
我只能想到
@Test
注釋方法之前必須創建瀏覽器的事實(使用@Before
創建WebDriver實例和@After
關閉會話) 我不太清楚選項3是否會遇到可能出現的問題。 如果我在每個方法之后關閉會話,那么在此節點完成之前的節點之前,網格服務器可能實際上在此節點上打開一個具有全新類的新會話。 雖然測試是相互獨立的,但我仍然認為這是潛在的危險。
有沒有人在這里積極使用多線程Selenium測試服,並可以指導我什么是正確的設計?
一般來說,我同意:
如果已經使用像JUnit這樣的框架,手動處理線程可能是個壞主意
但是,看看你提到的Parallelized
runner和junit 4.12中@Parametrized
內部實現是可能的。
每個測試用例都計划執行。 默認情況下,junit在單線程中執行測試用例。 Parallelized
擴展Parametrized
以單線程測試調度程序被多線程調度程序替換,因此,為了理解這是如何影響Parametrized
測試用例運行的方式,我們必須查看JUnit Parametrized
源:
好像:
@Parametrized
測試用例被拆分為每個測試參數的TestWithParameters
組 Runner
每個TestWithParameters
實例創建並安排執行(在這種情況下Runner
實例是專門的BlockJUnit4ClassRunnerWithParameters
) 實際上, 每個@Parametrized測試用例都會生成一組要運行的測試實例(每個參數都有一個實例),並且每個實例都是獨立調度的,因此在我們的情況下(使用WebDriver
實例作為參數進行Parallelized
和@Parametrized
測試)將執行多個獨立測試在每個WebDriver
類型的專用線程中。 這很重要,因為我們可以將特定的WebDriver
實例存儲在當前線程的范圍內。
請記住,此行為依賴於junit 4.12的內部實現細節,並且可能會更改 (例如,請參閱RunnerScheduler
中的RunnerScheduler
)。
我看下面的例子。 它依賴於提到的JUnit行為,並使用ThreadLocal
存儲在相同案例組中的test之間共享的WebDriver
實例。 只有ThreadLocal
技巧只是初始化它一次(在@Before中)並銷毀每個創建的實例(在@AfterClass中)。
package example.junit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
/**
* Parallel Selenium WebDriver example for http://stackoverflow.com/questions/30353996/selenium-and-parallelized-junit-webdriver-instances
* Parallelized class is like http://hwellmann.blogspot.de/2009/12/running-parameterized-junit-tests-in.html
*/
@RunWith(Parallelized.class)
public class ParallelSeleniumTest {
/** Available driver types */
enum WebDriverType {
CHROME,
FIREFOX
}
/** Create WebDriver instances for specified type */
static class WebDriverFactory {
static WebDriver create(WebDriverType type) {
WebDriver driver;
switch (type) {
case FIREFOX:
driver = new FirefoxDriver();
break;
case CHROME:
driver = new ChromeDriver();
break;
default:
throw new IllegalStateException();
}
log(driver, "created");
return driver;
}
}
// for description how to user Parametrized
// see: https://github.com/junit-team/junit/wiki/Parameterized-tests
@Parameterized.Parameter
public WebDriverType currentDriverType;
// test case naming requires junit 4.11
@Parameterized.Parameters(name= "{0}")
public static Collection<Object[]> driverTypes() {
return Arrays.asList(new Object[][] {
{ WebDriverType.CHROME },
{ WebDriverType.FIREFOX }
});
}
private static ThreadLocal<WebDriver> currentDriver = new ThreadLocal<WebDriver>();
private static List<WebDriver> driversToCleanup = Collections.synchronizedList(new ArrayList<WebDriver>());
@BeforeClass
public static void initChromeVariables() {
System.setProperty("webdriver.chrome.driver", "/path/to/chromedriver");
}
@Before
public void driverInit() {
if (currentDriver.get()==null) {
WebDriver driver = WebDriverFactory.create(currentDriverType);
driversToCleanup.add(driver);
currentDriver.set(driver);
}
}
private WebDriver getDriver() {
return currentDriver.get();
}
@Test
public void searchForChromeDriver() throws InterruptedException {
openAndSearch(getDriver(), "chromedriver");
}
@Test
public void searchForJunit() throws InterruptedException {
openAndSearch(getDriver(), "junit");
}
@Test
public void searchForStackoverflow() throws InterruptedException {
openAndSearch(getDriver(), "stackoverflow");
}
private void openAndSearch(WebDriver driver, String phraseToSearch) throws InterruptedException {
log(driver, "search for: "+phraseToSearch);
driver.get("http://www.google.com");
WebElement searchBox = driver.findElement(By.name("q"));
searchBox.sendKeys(phraseToSearch);
searchBox.submit();
Thread.sleep(3000);
}
@AfterClass
public static void driverCleanup() {
Iterator<WebDriver> iterator = driversToCleanup.iterator();
while (iterator.hasNext()) {
WebDriver driver = iterator.next();
log(driver, "about to quit");
driver.quit();
iterator.remove();
}
}
private static void log(WebDriver driver, String message) {
String driverShortName = StringUtils.substringAfterLast(driver.getClass().getName(), ".");
System.out.println(String.format("%15s, %15s: %s", Thread.currentThread().getName(), driverShortName, message));
}
}
它將打開兩個瀏覽器並同時在每個瀏覽器窗口中執行三個測試用例 。
控制台將打印如下內容:
pool-1-thread-1, ChromeDriver: created
pool-1-thread-1, ChromeDriver: search for: stackoverflow
pool-1-thread-2, FirefoxDriver: created
pool-1-thread-2, FirefoxDriver: search for: stackoverflow
pool-1-thread-1, ChromeDriver: search for: junit
pool-1-thread-2, FirefoxDriver: search for: junit
pool-1-thread-1, ChromeDriver: search for: chromedriver
pool-1-thread-2, FirefoxDriver: search for: chromedriver
main, ChromeDriver: about to quit
main, FirefoxDriver: about to quit
您可以看到為每個工作線程創建了一次驅動程序,並在最后銷毀。
總而言之,我們在執行線程的上下文中需要@BeforeParameter
和@AfterParameter
這樣的東西,快速搜索顯示這樣的想法已經在Junit中注冊為問題
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.