簡體   English   中英

Selenium和Parallelized JUnit - WebDriver實例

[英]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這樣的框架,手動處理線程可能是個壞主意。

我的問題

做到這一點的正確設計是什么?

我只能想到

  1. 使這里建議的工作
  2. 編寫一個@Test注釋方法,執行所有其他方法,然后使用@After實現與@AfterClass相同的方法
  3. 將構造函數參數保存在成員變量中,並處理在執行每個@Test注釋方法之前必須創建瀏覽器的事實(使用@Before創建WebDriver實例和@After關閉會話)

我不太清楚選項3是否會遇到可能出現的問題。 如果我在每個方法之后關閉會話,那么在此節點完成之前的節點之前,網格服務器可能實際上在此節點上打開一個具有全新類的新會話。 雖然測試是相互獨立的,但我仍然認為這是潛在的危險。

有沒有人在這里積極使用多線程Selenium測試服,並可以指導我什么是正確的設計?

一般來說,我同意:

如果已經使用像JUnit這樣的框架,手動處理線程可能是個壞主意

但是,看看你提到的Parallelized runner和junit 4.12中@Parametrized內部實現是可能的。

每個測試用例都計划執行。 默認情況下,junit在單線程中執行測試用例。 Parallelized擴展Parametrized以單線程測試調度程序被多線程調度程序替換,因此,為了理解這是如何影響Parametrized測試用例運行的方式,我們必須查看JUnit Parametrized源:

https://github.com/junit-team/junit/blob/r4.12/src/main/java/org/junit/runners/Parameterized.java#L303

好像:

  1. @Parametrized測試用例被拆分為每個測試參數的TestWithParameters
  2. 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM