硒和并行化的JUnit

设置

所以,基本上我试图实现使用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.whateverdriver.get().whatever调用driver.get().whatever在我的代码。

现在,为了解决这个问题的最终目的,我还写了一个@AfterClass方法,它会调用driver.get().quit(); 由于该变量是静态的,因此编译器现在已经接受了它。

然而,测试这会导致意外的行为。 我有一个Selenium Grid设置,其中有两个节点在远程机器上运行。 我以前曾经按照预期运行这个设置,但现在浏览器都被垃圾邮件所覆盖,测试失败。 (虽然2个浏览器应该运行而不是8个以上)

我建议这个解决方案的线程有人发表评论说,如果已经使用像JUnit这样的框架来手动处理线程可能是一个坏主意。

我的问题

什么是正确的设计来做到这一点?

我只能想到

  • 使这里建议的工作
  • 编写一个执行所有其他方法的@Test注释方法,然后使用@After实现与@AfterClass相同的方法
  • 将构造函数参数保存在成员变量中,并处理这样一个事实,即必须在每个执行了@Test注释的方法之前创建一个浏览器(使用@Before创建WebDriver实例并使用@After关闭会话)
  • 我不知道选项3是否会遇到可能的问题。 如果我在每种方法之后关闭会话,那么网格服务器可能实际上在此节点完成之前打开一个全新类的新会话。 虽然测试是相互独立的,但我仍然觉得这是潜在的危险。

    有人在这里积极使用多线程Selenium测试套装,并可以指导我什么是适当的设计?


    总的来说,我同意:

    如果已经使用像JUnit这样的框架,手动处理线程可能不是个好主意

    但是,看看你提到的Parallelized runner和junit 4.12的内部实现@Parametrized是可能的。

    每个测试用例都计划执行。 默认情况下,junit在单线程中执行测试用例。 Parallelized extends Parametrized以这种方式将单线程测试调度程序替换为多线程调度程序,因此,为了理解这如何影响Parametrized测试用例的运行方式,我们必须查看JUnit Parametrized源代码:

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

    好像:

  • 对于每个测试参数, TestWithParameters @Parametrized测试用例被分成TestWithParameters
  • Runner每个TestWithParameters实例创建并计划执行(在这种情况下, Runner实例是专用的BlockJUnit4ClassRunnerWithParameters
  • 实际上, 每个@Parametrized测试用例都会生成一组运行的测试实例(每个参数都有一个实例),并且每个实例都是独立调度的,所以在我们的示例中(使用WebDriver实例作为参数进行Parallelized@Parametrized测试)将执行多个独立测试在每个WebDriver类型的专用线程中。 这很重要,因为允许我们在当前线程的作用域中存储特定的WebDriver实例。

    记住,这种行为依赖于junit 4.12的内部实现细节,并且可能会改变 (例如,请参阅RunnerScheduler中的RunnerScheduler )。

    以我看下面的例子。 它依赖于提到的JUnit行为,并使用ThreadLocal来存储在相同案例组中测试之间共享的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中注册为问题

    链接地址: http://www.djcxy.com/p/52331.html

    上一篇: Selenium and Parallelized JUnit

    下一篇: test cases for login page using selenium ; java and Eclipse IDE