И так, ситуация следующая: имеем на проекте фреймворк для мобильной автоматизации = Java + TestNG + Appium, наша программа работает на Android и iOS.
Архитектура задизайнена таким образом, что мы не использует напрямую AndroidDriver или IOSDriver, а только работаем с AppiumDriver.
И все было хорошо до момента, пока не стало нужно протестировать image upload. В Appium есть специальный метод - driver.PushFile() , вот только он работает с дочерними класами AndroidDriver или IOSDriver.
Думала, что удастся закастить AppiumDriver к AndroidDriver:
((AndroidDriver) DriverProvider.getDriver()).pushFile("/sdcard/Pictures", new File("src/test/resources/Photos/Alpine_Pass.jpg"));
или
(AndroidDriver) DriverProvider.getDriver().pushFile("/sdcard/Pictures", new File("src/test/resources/Photos/Alpine_Pass.jpg"));
но, увы, оба варианта выдают
ClassCastException: io.appium.java_client.AppiumDriver cannot be cast to io.appium.java_client.android.AndroidDriver
(пока не получилось нагуглить рабочий вариант)
Подскажите пожалуйста, что я делаю НЕ так и как можно разрешить эту проблему.
Нет я имею ввиду, что у вас ссылка AppiumDriver ссылается на объекты androiddriver и iosdriver или же на только на объект appiumdriver? Если только реализация через объект AppiumDriver, то тут и понятно почему происходит исключение.
увы и ах, но фреймворк достался нам в наследство, поэтому и возникла необходимость рефакторить существующую архитектуру. Но я все же надеялась, что ето можно сделать меньшей кровью
Как вы получаете драйвер изначально ? DriverProvider.getDriver() - что происходит в методе getDriver() ?
Вам нжно написать такой метод который исходя из того что вам сейчас нужно возвращался Android или iOS driver.
Всего один метод
public AppiumDriver getDriver(boolean isAndroidDriverNeeded){
if (isAndroidDriverNeeded){
return new AndroidDriver<>(new URL(yourAppiumUrl), capabilities);
} else {
return new iOSDriver<>(new URL(yourAppiumUrl), capabilities);
}
}
Чтото типа такого и не надо будет переписывать все и сможете кастить АппиумДрайвер до Android/iOS драйвера
Вроде должно сработать так. В крайнем случае можно фабричный метод сделать, который на вход получает AppiumDriver, а на выход создаёт новый iOS или Andriod. Но я бы такой костыль не стал делать (создание нового драйера через фабричный метод).
Подойдет.
Ведь ThreadLocal означает что каждый тред имеет свое значение этого поля
Вы главное пробуйте. Основная ваша проблема в том что АппиумДрайвер не знает он Андроид или iOS и поетому не может скастоваться.
У тебя создается объект класс AppiumDriver, т.е. с полями, методами и т.д. которые находятся только в классе AppiumDriver. Если ты пытаешь сделать каст такого объекта к типу дочернего класса, то будет выкинуто исключение т.к. класс AppiumDriver не знает ничего о содержимом классов AndroidDriver и IOSDriver.
Так я выше написал почему не работает. Если заменить в моём примере Object на AppiumDriver, а Integer на AndroidDriver или IOSDriver, то это будет как раз ваша ситуация. Каст возможен в первом случае и невозможен во втором.
Можно рассмотреть каст ещё вот на примере Object, Number, Integer и Double. Object o = new Integer(1); // допустимо Integer i = new Object(); // недопустимо! мы не можем объявлять ссылку (переменную) на родительский объект, потому что у объявленного дочернего типа могут быть дополнительные методы, которых нет в родительском. Integer i = new Double(0.0); // аналогично так тоже сделать нельзя, т.к. тип Double не является дочерним для Integer.
При этом если мы в переменную типа Object помещаем объект типа Integer или Double, то объект остаётся того же типа, он не становится объектом, но по ссылке напрямую к его дополнительным методам обратиться будет нельзя, т.к. код считает что имеет дело с обычным типом Object. Вот для этого и делается кастинг. Он приводит ссылку к типу объекта.
Например, мы можем указать вот так: Number n = new Integer(1);
а дальше сделать каст переменно n до типа Integer. Integer i = (Integer) n; // это допустимый каст, т.к. ссылка n ведёт на объект типа Integer.