appium / java-client

Java language binding for writing Appium Tests, conforms to W3C WebDriver Protocol
Apache License 2.0
1.18k stars 752 forks source link

Cannot cast WebDriver object to AndroidDriver object. java.lang.ClassCastException exception is thrown #2105

Closed sudo-naim closed 4 months ago

sudo-naim commented 5 months ago

Description

When using the decorator to add listener hooks the decorator function returns a WebDriver object. When trying to cast WebDriver to AndroidDriver an exception is thrown:

Exception message: java.lang.ClassCastException: class net.bytebuddy.renamed.java.lang.Object$ByteBuddy$pMV59rfO cannot be cast to class io.appium.java_client.android.AndroidDriver (net.bytebuddy.renamed.java.lang.Object$ByteBuddy$pMV59rfO is in unnamed module of loader net.bytebuddy.dynamic.loading.ByteArrayClassLoader @33899f7a; io.appium.java_client.android.AndroidDriver is in unnamed module of loader 'app')

Environment

Details

Code To Reproduce Issue [ Good To Have ]

AndroidDriver driver = (AndroidDriver) new EventFiringDecorator(new Listener()).decorate(webDriverBuilder.getAndroidDriver());

Exception Stacktraces

Exception message: java.lang.ClassCastException: class net.bytebuddy.renamed.java.lang.Object$ByteBuddy$pMV59rfO cannot be cast to class io.appium.java_client.android.AndroidDriver (net.bytebuddy.renamed.java.lang.Object$ByteBuddy$pMV59rfO is in unnamed module of loader net.bytebuddy.dynamic.loading.ByteArrayClassLoader @33899f7a; io.appium.java_client.android.AndroidDriver is in unnamed module of loader 'app')

valfirst commented 5 months ago

@Naim995 please try the following approach:

AndroidDriver driver = (AndroidDriver) new EventFiringDecorator<AndroidDriver>(AndroidDriver.class, new Listener()).decorate(webDriverBuilder.getAndroidDriver());
mykola-mokhnach commented 5 months ago

@valfirst Not sure if this is going to work. The decorator is a runtime class that only consists of public interfaces that the class being decorated implements. That is why a runtime typecast to a class does not work. It only works if casting to an interface.

Possible solution: https://github.com/appium/java-client/blob/master/docs/The-event_firing.md#createproxy-api-since-java-client-830

valfirst commented 5 months ago

new event listeners mechanism in Selenium is a complete nightmare 😞

mykola-mokhnach commented 5 months ago

new event listeners mechanism in Selenium is a complete nightmare 😞

The whole dynamic classes topic in Java is complicated, contains lot of dark magic and requires an advanced developer. But it allows to create things that would be impossible otherwise. So, welcome to the dark side.

sudo-naim commented 5 months ago

@valfirst @mykola-mokhnach I found a workaround on selenium docs

So i resolved by using the following code:

public class AndroidFirstTest {
    DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
    Android driver;

    @BeforeMethod(alwaysRun = true)
    public void beforeMethod() throws IOException {
        desiredCapabilities.setCapability("platformName", "Android");

        driver = createProxy(
                        AndroidDriver.class,
                        new Object[]{new URL("http://localhost:4444"), desiredCapabilities},
                        new Class[] {URL.class, Capabilities.class},
                        new Listener()
                );
    }

    @Test
    public void appium_first_test() {
            //write your test here
    }

}

public class Listener implements MethodCallListener {
    protected Logger log = LoggerFactory.getLogger(this.getClass());

    StringBuilder acc = new StringBuilder();
    public void beforeCall(Object target, Method method, Object[] args) {
        if (!method.getName().equals("get")) {
            throw new NotImplementedException();
        }
        acc.append("beforeCall ").append(method.getName()).append("\n");
    }
}