0

我完全赞成有人为这个特定问题推荐一个更好的标题。我也非常愿意努力简化我描述问题的方式。

上下文:我有一个自动化设置,允许通过属性文件配置浏览器。因此,如果有人在该文件中有“browser=chrome”,那么WebDriver应该实例化的特定实例是ChromeDriver.

我还在使用WebDriverManager,您可以在其中下载特定WebDriver类型的二进制文件。所以在这种情况下,我只想下载该属性文件中的任何浏览器驱动程序。因此,如果那是 Chrome,我想使用ChromeDriverManager.

当然,这里的关键是我必须概括所有这些,因为我不知道有人会使用什么。但出于我的问题的目的,为了说明问题,让我们坚持使用这些移动部件:“chrome” ChromeDriver,,,ChromeDriverManager

代码:

我有一个driverMap包含WebDriver与浏览器名称关联的类的实例。

private static final Map<String, Class<?>> driverMap = new HashMap<String, Class<?>>() {
    {
        put("chrome", ChromeDriver.class);
        put("firefox", FirefoxDriver.class);
    }
};

我也有一个将类与特定类driverManager相关联的方法。BrowserManagerWebDriver

private static final Map<Class<?>, Class<?>> driverManager = new HashMap<Class<?>, Class<?>>() {
    {
        put(ChromeDriver.class, ChromeDriverManager.class);
        put(FirefoxDriver.class, FirefoxDriverManager.class);
    }
};

只是为了更多的上下文,所有这些都在一个名为的类中Driver,它的开头是这样的:

public final class Driver {
    private static WebDriver driver;
    private static BrowserManager manager;
   ....
}

这两个变量在这里与下一位相关。调用一个add方法来将特定的浏览器配置添加到测试中。所以这里是那个方法,它显示了当浏览器添加到混合中时如何使用上述方法:

public static void add(String browser, Capabilities capabilities) throws Exception {
    Class<?> driverClass = driverMap.get(browser);
    Class<?> driverBinary = driverManager.get(driverClass);

    manager = (BrowserManager) driverBinary.getConstructor().newInstance(); /// <<--- PROBLEM

    driver = (WebDriver) driverClass.getConstructor(Capabilities.class).newInstance(capabilities);
}
  • 你可以看到我使用driverClass,这将是这样的org.openqa.selenium.chrome.ChromeDriver

  • 你可以看到我使用driverBinary,这将是这样的io.github.bonigarcia.wdm.ChromeDriverManager

但我评论了上面我有问题的那一行。

问题:你可以看到我使用一个driver变量来存储WebDriver实例和一个manager变量来存储BrowserManager实例。

以下是我在以下情况下这样做的方式和原因driver

所以这样做是让我得到ChromeDriver更一般的( )的适当类型( WebDriver)。因此,在我的driver变量上,我能够将反射调用投射到WebDriver并因此引用driver,就好像它是那个实例一样。

我不能做同样的事情manager

而且我不知道这是否是因为那个特定的 Java 库是如何工作的。具体来说:

所以我不能调用方法,manager就好像它是特定类型的BrowserManager(like ChromeDriverManager) 一样driver(这是特定类型的WebDriver, like ChromeDriver)。

这似乎是因为最终WebDriver是一个接口,但却BrowserManager是抽象的。

所以我不知道如何达到我想要的效果。具体来说,我想要的效果是调用相当于这个:

ChromeDriverManager.getInstance().setup();

但是我必须使用反射来做到这一点,因为我不知道我将使用哪个经理。所以理想情况下我想要它,这样我就可以做到这一点:

manager.getInstance().setup();

我不知道我可以为了manager工作而放弃什么。或者我不知道一旦我确定了那个类是什么,我是否可以转换到一个特定的类。

我可以完全放弃使用 WebDriverManager,但它是一个很好的解决方案,我希望找到一些方法来做我需要的事情。

4

3 回答 3

1

所以我不知道如何达到我想要的效果。具体来说,我想要的效果是调用相当于这个:

ChromeDriverManager.getInstance().setup();

但是我必须使用反射来做到这一点,因为我不知道我将使用哪个经理。所以理想情况下我想要它,这样我就可以做到这一点:

manager.getInstance().setup();

我不知道为了让经理工作,我可以放弃什么。或者我不知道一旦我确定了那个类是什么,我是否可以转换到一个特定的类。

经过调查,我发现这ChromeDriverManager.getInstance()是一种静态方法。静态方法是在编译时绑定的,而不是在运行时绑定的,因此如果您在编译时不知道要调用哪个类的方法,则无法通过正常的方法调用表达式调用该方法。关键是你不知道。

但这是愚蠢的。该方法的重点是提供类的实例,注册BrowserManager为指定的特殊实例。尝试通过首先获取其他不需要的其他实例来尝试这样做是没有意义的,因为您也不需要类的实例来调用类的静态方法。

看起来具体的BrowserManager子类实现了这种getInstance()方法的模式。尽管它们不是多态的,因此不能保证存在,但您可以依赖模式来定位和反射地调用它们(而不是反射地调用构造函数)。例如,

    Class<?> driverBinary = driverManager.get(driverClass);

    try {
        // Retrieves a no-arg method of the specified name, declared by the
        // driverBinary class
        Method getInstanceMethod = driverBinary.getDeclaredMethod("getInstance");

        // Invokes the (assumed static) method reflectively
        BrowserManager manager = (BrowserManager) getInstanceMethod.invoke(null);

        manager.setup();
    } catch ( IllegalAccessException
            | IllegalArgumentException
            | InvocationTargetException
            | NoSuchMethodException
            | SecurityException e) {
        // handle exception
    }

BrowserManager您可以调用结果对象上声明的所有实例方法。特别是,您可以调用setup(),如图所示。

另一方面,如果您不需要将您的实例注册为特殊指定BrowserManager实例,那么您根本不需要经过getInstance()。您已经拥有的用于获取实例的方法足以让您获得一个实例,然后您可以setup()直接调用它的方法。我不确定没有注册实例是否BrowserManager会出现任何问题。

于 2017-05-12T19:51:04.537 回答
0

在约翰的评论和帮助下,这种方法是否有意义,我确实找到了一种稍微蛮力的方法来处理这个问题,那就是:

Class<?> driverBinary = driverManager.get(driverClass);

if (driverBinary.newInstance() instanceof ChromeDriverManager) {
    ChromeDriverManager.getInstance().setup();
}

所以在这里我摆脱了manager变量,只使用driverBinary实例来检查它是否是驱动程序管理器之一的实例。然后我可以为每个浏览器添加一系列 else if 条件。例如:

if (driverBinary.newInstance() instanceof ChromeDriverManager) {
    ChromeDriverManager.getInstance().setup();
} else if (driverBinary.newInstance() instanceof FirefoxDriverManager) {
    FirefoxDriverManager.getInstance().setup();
} else if (...) {
    ...
}

我说“蛮力”是因为我意识到这个解决方案并没有提供很多技巧。我还需要使用 John 提供的解决方案。

这些挑战在测试框架中出现了很多,您无法知道测试将在什么条件下运行。因此,能够制定更好或更差的方式来做这些事情似乎很有用。

以上内容目前显示在我的Driver类中。

于 2017-05-12T20:39:57.970 回答
0

WebDriverManager针对不同的浏览器有不同的driverManagerChromeDriverManager ,例如Chrome、FirefoxDriverManagerFirefox 等。此外,它有一个可以参数化的通用driverManager 。这个驱动程序是直接命名的WebDriverManager。driver方法getInstance()接受要使用的底层浏览器的 WebDriver 类(即ChromeDriverFirefoxDriver等):

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

import io.github.bonigarcia.wdm.WebDriverManager;

// ...

Class<? extends WebDriver> driverClass = ChromeDriver.class;

// ... other option:
// driverClass = FirefoxDriver.class;

WebDriverManager.getInstance(driverClass).setup();
WebDriver driver = driverClass.newInstance();

在这里您可以找到一个工作示例(使用具有相同测试逻辑的 Chrome 和 Firefox 的 JUnit 4 参数化测试,其中通用driverManager为 Chrome 和 Firefox 解析正确的二进制文件)

于 2017-05-13T12:42:10.333 回答