当前位置: 首页 > 面试题库 >

selenium和并行JUnit-WebDriver实例

令狐昂雄
2023-03-14
问题内容

设置

因此,基本上,我正在尝试实现使用JUnit并行运行的Selenium测试。

为此,我找到了这个JUnit运行器。它真的很好,我非常喜欢。

但是,我在处理WebDriver实例时遇到问题。

我想要的是

@Test执行方法之前,每个WebDriver元素应为每个类创建一次。

从逻辑上讲,我可以为此使用类构造函数。实际上,这确实是我测试的要求,因为我需要利用,@Parameters以便可以相应地创建WebDriver实例(Chrome,FF,IE
…)。

问题

问题是我希望在 类* 完成后而不是在 每种 方法
完成后清除(driver.quit())WebDriver实例。但是我不能使用,因为我不能使WebDriver成为静态成员,因为每个类实例必须使用自己的实例(否则测试将尝试在同一浏览器中运行)。

@Test*@AfterClass

可能的解决方案

我发现了一个可能的建议在这里通过 Mrunal Gosar。按照他的建议,我将WebDriver改为了ADriver static ThreadLocal<WebDriver>,然后在每个构造函数中使用创建它的实例

 // in the classes constructor

driver = new ThreadLocal<WebDriver>() {
           @Override
           protected WebDriver initialValue() {
                 return new FirefoxDriver(); /
           }
};

针说我用代码替换了每个driver.whatever调用driver.get().whatever

现在,为了解决此问题的最终目的,我还编写了一个@AfterClass方法,该方法将driver.get().quit();被编译器接受,因为变量是静态的。

但是,对此进行测试会导致意外行为。我有一个SeleniumGrid设置,其中有2个节点在远程计算机上运行。我曾按预期运行此设置,但现在浏览器到处都是垃圾邮件,并且测试失败。(虽然应该运行2个浏览器,但要打开8个以上的浏览器)

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

我的问题

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

我只能想到

  1. 使这里建议的工作
  2. 编写一个执行所有其他方法的带@Test注释的方法,然后使用@After来实现与@AfterClass相同的方法
  3. 将构造函数参数保存在成员变量中,并处理以下事实:我必须在@Test执行每个带注释的方法之前创建一个浏览器(@Before用于创建WebDriver实例并@After关闭会话)

我不太确定选项3是否会遇到可能的问题。如果我在每种方法之后都关闭了会话,那么在该节点完成之前的会话之前,Grid-
Server可能实际上在该节点上打开了一个具有全新类的新会话。尽管测试相互独立,但我仍然觉得这是潜在的危险。

在座的任何人都在积极使用多线程Selenium测试服并且可以指导我什么是正确的设计吗?


问题答案:

总的来说,我同意:

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

但是,看看Parallelized您提到的运行器以及@Parametrizedjunit 4.12的内部实现,这是可能的。

每个测试用例都计划执行。默认情况下,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. 为的每个TestWithParameters实例Runner创建并计划执行(在这种情况下,Runner实例是专门的 BlockJUnit4ClassRunnerWithParameters

实际上, 每个@Parametrized测试用例都会生成一组要运行的测试实例(每个参数一个实例),并且每个实例都是独立安排的,
因此在我们的案例中(Parallelized@ParametrizedWebDriver实例作为参数进行测试),将在专用线程中执行多个独立测试,以用于每种WebDriver类型。这很重要,因为它允许我们WebDriver在当前线程的范围内存储特定实例。

记住,此行为依赖于junit 4.12的内部实现细节,并且可能会发生变化 (例如,参见中的注释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中注册为问题。



 类似资料:
  • 问题内容: 设置 因此,基本上,我正在尝试实现使用JUnit并行运行的Selenium测试。 为此,我找到了这个JUnit运行器。它真的很好,我非常喜欢。 但是,我在处理WebDriver实例时遇到问题。 我想要的是 在执行方法之前,应为每个类创建一次每个WebDriver元素。 逻辑上,我可以为此使用类构造函数。实际上,这确实是我测试的要求,因为我需要利用,以便可以相应地创建WebDriver实

  • 引自 ChromeDriver - WebDriver for Chrome: WebDriver 是一款开源的支持多浏览器的自动化测试工具。 它提供了操作网页、用户输入、JavaScript 执行等能力。 ChromeDriver 是一个实现了 WebDriver 与 Chromium 联接协议的独立服务。 它也是由开发了 Chromium 和 WebDriver 的团队开发的。 配置 Spec

  • 问题内容: 这是我使用Selenium 2库的第二天,Unicode的痛苦似乎从未消退。 我只是在做最基本的操作,想打印页面源代码: 果然,我得到一个错误: 我如何将此编码为? 问题答案: 根据这个类似的问题,您可以选择。 您可以将源转换为在此过程中丢失Unicode字符的所有ascii。 或者,我认为您会更喜欢这种方式,可以将其编码为utf-8,如下所示: 。

  • 问题内容: 我的网页中有9行6列的表格。我想搜索文本“ MakeGoodDisabled- Programwise_09_44_38_461(n)”并获取单元格的xpath。我使用了以下内容,但由于无法在页面上找到文本而失败。你能帮忙吗?我正在使用Selenium Webdriver Junit对此进行编码。 问题答案: 我的意图是在表中查找文本并在同一行中获取相应的下一列值。我以为我将用所需的列

  • 我正在尝试用Web驱动集线器和TestNG并行机制设置Web驱动程序并行执行。我正面临线程的问题 我有一个扩展TestBaseSetUp的类,它有一个Beforemethod和Aftermethod,并设置为始终运行。对于Web驱动程序的并行执行,我想使用线程本地,但是@测试和@之前/@之后方法在不同的线程中。所以如果我在我的TestBaseSetUp中设置Web驱动程序为线程本地,并尝试进入我的