I am all for someone recommending a better title for this particular question. I'm also more than open to working to simplify how I describe the problem.
Context: I have an automation setup where I'm allowing the browser to be configured via a properties file. So if someone has "browser=chrome" in that file, then the specific WebDriver
instance that should be instantiated is ChromeDriver
.
I'm also using WebDriverManager wherein you can download the binaries for particular WebDriver
types. So in this case, I only want to download whatever browser driver is in that properties file. So if that's Chrome, I want to use ChromeDriverManager
.
The key thing here, of course, is that I have to generalize all this because I don't know what someone is going to use. But for purposes of my question here, and to show the problem, let's stick with these moving parts: "chrome", ChromeDriver
, ChromeDriverManager
.
Code:
I have a driverMap
that holds an instance of a WebDriver
class that is associated with a browser name.
private static final Map<String, Class<?>> driverMap = new HashMap<String, Class<?>>() {
{
put("chrome", ChromeDriver.class);
put("firefox", FirefoxDriver.class);
}
};
I also have a driverManager
that associates a BrowserManager
class with a particular WebDriver
class.
private static final Map<Class<?>, Class<?>> driverManager = new HashMap<Class<?>, Class<?>>() {
{
put(ChromeDriver.class, ChromeDriverManager.class);
put(FirefoxDriver.class, FirefoxDriverManager.class);
}
};
Just for more context, all of this is in a class called Driver
and it starts like this:
public final class Driver {
private static WebDriver driver;
private static BrowserManager manager;
....
}
Those two variables are relevant here for the next bit. An add
method is called to add a particular browser configuration to the tests. So here is that method, which shows how the above are used when a browser is added to the mix:
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);
}
You can see I use
driverClass
, which will be something like this:org.openqa.selenium.chrome.ChromeDriver
.You can see I use
driverBinary
, which will be something like this:io.github.bonigarcia.wdm.ChromeDriverManager
.
But I commented the line above where I have a problem.
Problem: You can see I use a driver
variable to store the WebDriver
instance and a manager
variable to store the BrowserManager
instance.
Here's how and why I'm doing that in the case of driver
:
So what that does is get me the appropriate type (ChromeDriver
) of the more general (WebDriver
). Thus on my driver
variable, I am able to cast the reflection call to WebDriver
and thus reference driver
as if it was that instance.
I can't do the same for manager
.
And I don't know if that's because of how that particular Java library works. Specifically:
So I can't call methods on manager
as if it was a specific type of BrowserManager
(like ChromeDriverManager
) as I can for driver
(which is a specific type of WebDriver
, like ChromeDriver
).
This would seem to be because ultimately WebDriver
is an interface but BrowserManager
is abstract.
So I don't know how to achieve the effect I want. Specifically, the effect I want is to make a call equivalent to this:
ChromeDriverManager.getInstance().setup();
But I have to do that using the reflection since I don't know what manager I'll be using. So ideally I want it so that I can do this:
manager.getInstance().setup();
I don't know what I can cast down to in order to make manager
work. Or I don't know if I can cast to a specific class once I've determined what that class is.
I can just abandon using WebDriverManager entirely but it is a nice solution and I'm hoping to find some way to do what I need.