Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

Инициализация драйвера, где нужно делать и почему.

framework
Теги: #<Tag:0x00007f7b6d112930>

(denys shynkarenko) #1

Привет всем,
хочу для себя разобраться в теории. Где лучше всего и в каких случаях инициализировать драйвер.
я встречал несколько вариантов:

  • в самом тесте и передавать его в конструкторе в страницы
  • в самом тесте, но перед выполнением теста например аннотация в testng
  • в самой странице (и когда идет обращение к странице она поднимает драйвер)

Возможно есть еще какие то интересные варианты?

Мне нравится идея полностью изолировать логику тестов от драйвера, но подозреваю, что этот способ может иметь какие то недостатки.

Спасибо!


(Sergey Korol) #2

Ни один из перечисленных вариантов не является правильным. То, что вы описываете, является domain specific module.

Вот вроде бы и label верный стоит, и мысли касательно изоляции верные, но по-моему, вам для начала необходимо разобраться, что есть - framework, а также - почему тесты и страницы не имеют никакого отношения к этому понятию. А дальше можно поговорить и о том, где же собственно должен быть изолирован WebDriver.


(denys shynkarenko) #3

Про framework принято. На самом деле я установил такой лейбл потому, что он был наиболее близок к вопросу(наиболее близок тут стоит понимать так, что другие были вообще не релевантны:) ).

Можем принять описание фреймворка, которое приведено на этом же сайте
Что такое фрейворк автоматизации?. Я бы описал это как каркас, который позволяет нам разрабатывать наши тесты быстро и удобно не заботясь о том, что для тестов нужно.

Я согласен с положением, что тесты и страницы не являются частью framework.


(Sergey Korol) #4

Верно, каркас. Теперь давайте попытаемся понять, из чего же должен состоять каркас, чтобы обеспечить упомянутую скорость и удобство в разработке, а также - надежность вашим тестам, независимо от AUT?


(denys shynkarenko) #5

Метод Сократа это серьезно:)

Итак каркас, должен состоять из уже существующих классов и методов, которые помогут нам работать с данными, делать отчеты, делать логирование. Так же должны быть какие то средства для работы с нашим инструментом, в данном случае с драйвером я бы назвал это driver handler.
Допустим у нас есть какой то еще модуль или компонент который позволяет управлять драйвером, запускать его, останавливать.
Я видел некоторые примеры как вот http://protesting.ru/automation/practice/pageobject_selenium.html.
Там есть класс Context который отвечает за запуск, остановку драйвера. Для меня это и есть handler.
Однако все равно потом инициализация происходит в самом тесте -

   public void setUp() {
        // Инициализация контекста.
        Context.initInstance(Context.BROWSER_IE, "http://www.testtesttestlogin.com");
    }

Именно это мне и не понятно, мы все равно получается зависим в наших тестах от драйвера (даже если вынести это все в базовый класс).

Спасибо за внимание к вопросу.


(Sergey Korol) #6

А вы не ищите примеры, противоречащие собственным умозаключениям. :slight_smile: Вы привязываетесь к конкретной реализации, которая могла быть написана на коленке чисто в учебных целях.

Положим, что у вас есть некий driver handler, который расположен внутри каркаса.
Возникает ряд вопросов:

  • По какому условию должна происходить инициализация / закрытие драйвера?
  • Действительно ли тестам необходим драйвер в пределах рассматриваемого уровня абстракции?
  • Каким модулям каркаса на самом деле необходим драйвер?
  • Каким образом установить гибкую связь между модулями, которым необходим драйвер?

(Roma Marinsky) #7

если хочешь попарить и потратить мимнимум пол года на написание стабильной обёртки над селениумом и утилит для проектика с автотестами. То у тебя обязательно должны быть классы такие: BrowserFactory - с набором браузеров, свойств для них, каких-то ещё действией над браузерами; WebDriverHolder - с инстансом браузера; WebDriverInstances - в котором будет сетер браузера, гетер вебдрайвера и например "InheritableThreadLocal"для возможности ранить тесты многопоточно.

А если времени нет такого для “свобододумия”(хотя костылеклепания вообще), лучше используй готовые решения selenide, JDI(epam), прочие готовые фрейворки(обёртки над селениумом) или же в пайтон мире RobotFramework
хотя в пайтон не знаю что лучше использовать, может питонист тебе подскажет готовые удобные фреймворки


(Goshko Nazar) #8

Для начала, Вы ответься себе на вопросы о концепции вашего тест приложения в целом, как вы будете его строить и по каким канонам. Возможно щас закритикуют, но для автотестов красота и гармония кода, как и его скорость (утрированно, так как большую часть мы не в коде, а в поисках елементов на странице) имею малую долю. Тесты должны в первую очередь быть поддерживаемые. Да, код тоже должен быть ок, иметь структурность, какие никакие патерны (страница, елемент), что бы Вы сами в нем не утонули, а значит что код должен быть не сложным, а простым и не тяжелым, а легким (Александр Соловьев, привет тебе)
Теперь о драйвере.

  1. Если вы не следуете каким либо патернам, идеомам, и опыту использования DSL подхода, то можете передавать драйвер как угодно и где угодно, смотрим правде в глаза - многие так начинали, и потом сами озонавали глупость. Собственный опыт так же необходим.
  2. Если вы понимаете что запуск драйвера является инфраструктурной конфигурируемой частью, то вы явно сможете описать синглтон для базового класса ваших страниц/елементов. Тем не менее вам так же прийдется дополнить ваши классы серьезным базовым функционалом для работы с елементами, не всегда это удобно, зато смотришь и любуешь - насколько прозрачный DSL вышел.
  3. Если вы следуете из композиционной стратегии вашего приложения, то драйвер вы передадите как агрумент главному классу приложения, а для тестов выставите публичные методы и атрибуты, передав приложение как фикстуру для тест оболочки, которая будет существовать до конца тестов - сессионная фикстура.

далее ИМХО: Я считаю что каждый для себя должен выбрать любой ему удобный подход, так как писать тесты именно ему, и поддерживать тоже. Если разработка ведется командой, то с ними этот вопрос так же необходимо решать. В любом случае, Вы так же должны понимать, что рано или позно “кирпич падает”, а работа заканчивается, и после Вас кто то другой будет это все поддерживать, так сделайте же таким образом, что бы уши у Вас не горели.
Что касается шибко умных программистов, и дзен-кодинга, то я думаю что тесты нужно писать, а не затейливо выражаться с помощью средств языка программирования.


(Eugene Moskalenko) #9

Интересная темка завелась :slight_smile: Приведу свой пример, может кому-то пригодиться, или что-то дельное подскажите…

На мобильном (Appium) у меня реализовано немного криво, через ApplicationManager:

DriverSetup

public class DriverSetup {

    private static final Logger logger = LogManager.getLogger(DriverSetup.class);

    private static DriverSetup _instance = null;

    private AndroidDriver driver;
    private WebDriverWait wait;
    private URL           serverUrl;

    public int EXPLICIT_WAIT_TIME;
    public int DEFAULT_WAIT_TIME;
    public int IMPLICIT_WAIT_TIME;

    public String WAIT_ACTIVITY;
    public String APPIUM_PORT;

    public String NEW_COMMAND_TIMEOUT;
    public String DEVICE_READY_TIMEOUT;

    public String DEVICE_NAME;
    public String BROWSER_NAME;
    public String PLATFORM_VERSION;
    public String PLATFORM_NAME;
    public String APP_PKG;
    public String APP_ACTIVITY;
    public String AUTOMATION_INSTRUMENTATION;
    public String APPLICATION_NAME;
    public String PERFORMANCE_LOGGING;

    private DesiredCapabilities capabilities = new DesiredCapabilities();
    private Properties prop = new Properties();

    public DriverSetup() {

        this.loadConfigProp();
        this.setCapabilities();

        try {
            serverUrl = new URL("http://127.0.0.1:" + APPIUM_PORT + "/wd/hub");
        } catch (MalformedURLException e) {
            e.printStackTrace();
            logger.error(e);
        }
        driver = new AndroidDriver(serverUrl, capabilities);
        //EXPLICIT_WAIT_TIME
        wait = (WebDriverWait) new WebDriverWait(driver, DEFAULT_WAIT_TIME)
                .withMessage("Element was not found")
                .ignoring(MoveTargetOutOfBoundsException.class);
        //driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_TIME, TimeUnit.SECONDS);
        // IMPLICIT_WAIT_TIME
        driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_TIME, TimeUnit.SECONDS);
    }

    public static DriverSetup getInstance() {
        if (_instance == null) {
            _instance = new DriverSetup();
        }
        return _instance;
    }

    public AndroidDriver getDriver() {
        return driver;
    }

    public WebDriverWait getWait() {
        return wait;
    }

    public void loadConfigProp() {
        EXPLICIT_WAIT_TIME = Integer.parseInt(PropertyLoader.loadProperty("explicitWait"));
        DEFAULT_WAIT_TIME = Integer.parseInt(PropertyLoader.loadProperty("defaultWait"));
        IMPLICIT_WAIT_TIME = Integer.parseInt(PropertyLoader.loadProperty("implicitWait"));
        WAIT_ACTIVITY = PropertyLoader.loadProperty("waitActivity");
        APPIUM_PORT = PropertyLoader.loadProperty("appiumServerPort");
        NEW_COMMAND_TIMEOUT = PropertyLoader.loadProperty("newCommandTimeout");
        DEVICE_READY_TIMEOUT = PropertyLoader.loadProperty("deviceReadyTimeout");

        DEVICE_NAME = PropertyLoader.loadProperty("deviceName");
        BROWSER_NAME = PropertyLoader.loadProperty("browserName");
        PLATFORM_VERSION = PropertyLoader.loadProperty("platformVersion");
        PLATFORM_NAME = PropertyLoader.loadProperty("platformName");
        APP_PKG = PropertyLoader.loadProperty("applicationPackage");
        APP_ACTIVITY = PropertyLoader.loadProperty("applicationActivity");
        AUTOMATION_INSTRUMENTATION = PropertyLoader.loadProperty("automationInstumentation");
        APPLICATION_NAME = PropertyLoader.loadProperty("applicationPath");
        //PERFORMANCE_LOGGING = PropertyLoader.loadProperty("enablePerformanceLogging");
    }

    public void setCapabilities() {
        capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, DEVICE_NAME);
        capabilities.setCapability(MobileCapabilityType.BROWSER_NAME, BROWSER_NAME);
        capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, PLATFORM_VERSION);
        capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, PLATFORM_NAME);
        capabilities.setCapability(MobileCapabilityType.APP_PACKAGE, APP_PKG);

        if (prop.containsKey("waitActivity")) {
            capabilities.setCapability(MobileCapabilityType.APP_WAIT_ACTIVITY, WAIT_ACTIVITY);
        } else {
            capabilities.setCapability(MobileCapabilityType.APP_ACTIVITY, APP_ACTIVITY);
        }
//        if (!APPLICATION_NAME.isEmpty()) {
//            capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AUTOMATION_INSTRUMENTATION);
//            capabilities.setCapability(MobileCapabilityType.APP, new File(
//                    ClassLoader.getSystemResource(APPLICATION_NAME).getFile()).getAbsolutePath());
//        }
        capabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, NEW_COMMAND_TIMEOUT);
        capabilities.setCapability(MobileCapabilityType.DEVICE_READY_TIMEOUT, DEVICE_READY_TIMEOUT);
        //capabilities.setCapability("unicodeKeyboard", true);
    }

}

пока эксперементирую с вейтами и наверное стоит переделать на ENUM переменные и избавиться от такого loadConfigProp

ApplicationManager - тут у меня скрины инициализируются и сам драйвер с вейтом…

public class ApplicationManager {

    public AndroidDriver driver;
    public WebDriverWait wait;

    // Helpers
    private NavigationHelper navigationHelper;
    private DeviceHelper     deviceHelper;

    // Screens
    private SomeScreen    someScreen;

    public ApplicationManager() {
        driver = DriverSetup.getInstance().getDriver();
        wait = DriverSetup.getInstance().getWait();
    }

    /***************
     * Helpers
     ***************/

    public NavigationHelper getNavigationHelper() {
        if (navigationHelper == null) {
            navigationHelper = new NavigationHelper(this);
        }
        return navigationHelper;
    }

    public DeviceHelper getDeviceHelper() {
        if (deviceHelper == null) {
            deviceHelper = new DeviceHelper(this);
        }
        return deviceHelper;
    }

    /***************
     * Screens
     ***************/

    public SomeScreen getSomeScreen() {
        if (someScreen == null) {
            someScreen = new someScreen(this);
        }
        return somScreen;
    }

    /***************
     * Others
     ***************/

    public void stop() {
        if (driver != null) {
            driver.quit();
        }
    }
}

BaseScreen

public abstract class BaseScreen<T extends BaseScreen<T>> {

    private static final Logger logger = LogManager.getLogger(BaseScreen.class);

    protected ApplicationManager manager;
    protected AndroidDriver      driver;
    protected WebDriverWait      wait;

    public BaseScreen(ApplicationManager manager) {
        this.manager = manager;
        this.driver = manager.driver;
        this.wait = manager.wait;
    }

    public void loadScreen() {
        PageFactory.initElements(new AppiumFieldDecorator(driver), this);
    }

    @AndroidFindBy(className = "android.webkit.WebView")
    private WebElement androidWebView;

    @SuppressWarnings("unchecked")
    public T then() {
        return (T) this;
    }

    @SuppressWarnings("unchecked")
    public T with() {
        return (T) this;
    }

    @Step("Tap by element {0}")
    public BaseScreen tap(WebElement element) {
        element.click();
        return (T) this;
    }

    @Step("Tap with hold by element {0}")
    public T longTap(WebElement element, int longPressTime) {
        TouchAction action = new TouchAction(driver);
        action.longPress(element, longPressTime).release().perform();
        return (T) this;
    }

    @Step("Type text {1} in field {0}")
    public T typeTextInField(WebElement element, String text) {
        element.clear();
        element.sendKeys(text);
        hideKeyboard();
        return (T) this;
    }

    @Step("Select checkBox \"{0}\"")
    public T selectCheckBox(WebElement element) {
        if (!element.isSelected()) {
            element.click();
        }
        return (T) this;
    }

    public T goToAppMainScreen() {
        driver.navigate().back();
        return (T) this;
    }

    @Step("Hide keyboard")
    public T hideKeyboard() {
        Process p = null;
        try {
            p = Runtime.getRuntime().exec("adb shell dumpsys input_method | grep mInputShown");
            BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String outputText = "";

            while ((outputText = in.readLine()) != null) {

                if (!outputText.trim().equals("")) {
                    String keyboardProperties[] = outputText.split(" ");
                    String keyValue[] = keyboardProperties[keyboardProperties.length - 1].split("=");

                    String softkeyboardpresenseValue = keyValue[keyValue.length - 1];
                    if (softkeyboardpresenseValue.equalsIgnoreCase("false")) {
                        logger.info("Keyboard already closed");
                    } else {
                        driver.hideKeyboard();
                        logger.info("Keyboard closed");
                    }
                }
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return (T) this;
    }

    @Step("Rotate device screen: LANDSCAPE / PORTRAIT")
    protected T rotateScreen() {
        driver.rotate(ScreenOrientation.LANDSCAPE);
        return (T) this;
    }

    @Step("Switch to webview")
    protected T switchToWebView() {
        //wait.until(ExpectedConditions.visibilityOf(androidWebView));
        Set<String> contextSet = driver.getContextHandles();
        for (String contextName : contextSet) {
            if (!contextName.contains("NATIVE_APP")) {
                driver.context(contextName);
                break;
            }
        }
        return (T) this;
    }

    @Step("Take screenshot")
    protected T takeScreenShot(String fileName) {
        File file    = new File(fileName + ".png");
        File tmpFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
        try {
            FileUtils.copyFile(tmpFile, file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return (T) this;
    }
}

Собственно вот сам скрин:

public class SomeScreen extends BaseScreen<SomeScreen> {

    @AndroidFindBy(uiAutomator = "new UiSelector().text(\"Write Message\")")
    private WebElement writeMessageButton;

    public SomeScreen(ApplicationManager manager) {
        super(manager);
        loadScreen();
    }

    // методы тут идут по работе с элементами

   public SomeScreen sendMessages(String message) {
    tap(this.writeMessageButton);
    typeTextInField(this.sendMessagesField, message);
    tap(this.sendMessagesButton);
    return this;
  }

}

вот собственно мой тест:

public class SomeTest extends BaseTest {

    @Test(enabled = true, groups = "some groups", priority = 10)
    @Title("title")
    public void someTestName() {
        // --------------------- Test Data ----------------------//

        // --------------------- Test Case ----------------------//
        app.getSomeScreen()
                .sendMessages("qwerty")
                .goToAppMainScreen();
    }

}

конечно тетс сократил, там у меня еще контейнеры, в которых логика состредаточена для разных девайсов и разных имен и разных сообщений, но для примера будет понятно…

BaseTest

//@Listeners({ScreenshotListener.class, TestListener.class})
@Listeners({TestListener.class})
public class BaseTest {

    private static final Logger logger = LogManager.getLogger(BaseTest.class);

    protected ApplicationManager app;

    @BeforeSuite(alwaysRun = true)
    public void setUp() {
        app = new ApplicationManager();
    }

    @AfterSuite(alwaysRun = true)
    public static void createAllureProperties() {
        AllureProperties.create();
    }

    @AfterSuite(alwaysRun = true)
    public void tearDown() {
        //app.stop();
    }

}

Начал писать все это когда еще ничерта не понимал, и только начинал разбираться в автоматизации, поэтому не очень здорово тут все получилось, сейчас вот переписываю так, чтобы все ожидания и методы с UI элементами - перенести в отдельные классы.

Это так у меня под мобилку для Android, фреймворк такой, сейчас уже понял что не так, чуть опыта поднабрался, переписываю… На вебе там все посложнее будет, там у меня реализована многопоточность и куча браузеров и запуск под разными ОС…

При написании фреймворка в автоматизации для себя выделил такие вещи:

  1. весь код должен переиспользоваться так, чтобы в пейджах были только элементы и работа с ними
  2. при чтении тестов все должно быть предельно понятно, что происходит
  3. драйвер и ожидания не должны быть в тестах и не должны быть в пейджах
  4. все методы касающиеся элемнтов - должны находиться в своем классе
  5. все методы по ожиданиям - должны находится в своем классе
  6. фреймворк должен писаться так, чтобы я сначала проектировал тест, а из него уже генерировал методы. Проще придумать тест, каким он должен быть, а затем уже писать методы для теста
  7. везде должны соблюдаться уровни доступа

#10

А я инициализирую драйвер в TestBase классе, от которого наследуются все тесты и при запуске теста происходит инициализаци )

@Listeners({LogListener.class, TestReport.class})
public class TestBase {

    /**
     * Логгер
     */
    private static final Logger LOG = LogManager.getLogger(TestBase.class.getName());

    /**
     * Профайл FireFox
     */
    private String firefoxProfile;

    /**
     * Инстанс ВебДрайвера
     */
    protected static EventFiringWebDriver driver;

    /**
     * Инициализация параметров
     * @param context контекст выполнения
     */
    private void initParameters(ITestContext context){
        firefoxProfile = context.getCurrentXmlTest().getParameter("firefoxProfile");
    }

    /**
     * Инициализация драйвера
     */
    private void initDriver(){
        WebDriver wDriver = new FirefoxDriver(initCapabilities(firefoxProfile));
        driver = new EventFiringWebDriver(new StaleTolerantWrapper(wDriver).getDriver());
        driver.register(new EventHandler());
        driver.manage().window().maximize();
        driver.manage().timeouts().pageLoadTimeout(130, TimeUnit.SECONDS);
        driver.manage().timeouts().implicitlyWait(130, TimeUnit.SECONDS);
    }

    /** Инициализация свойств
     * @param profileName имя профайла
     * @return инстанс браузера
     */
    private DesiredCapabilities initCapabilities(String profileName){
        LOG.info("Используется профайл: " + profileName);
        DesiredCapabilities firefox;
        ProfilesIni allProfiles = new ProfilesIni();
        FirefoxProfile profile = allProfiles.getProfile(profileName);
        firefox = DesiredCapabilities.firefox();
        firefox.setCapability(FirefoxDriver.PROFILE, profile);
        return firefox;
    }

    /**
     * Получить инстанс драйвера
     * @return инстанс драйвера
     */
    public static EventFiringWebDriver getDriver(){
        return driver;
    }

    /**
     * Инициализация Теста
     * @param context контекст выполнения
     */
    @BeforeSuite (description = "Инициализация драйвера")
    public void initBase(ITestContext context){
        LOG.info("Старт firefox");
        initParameters(context);
        initDriver();
    }

    /**
     * По окончанию сьюта гасим браузер
     */
    @AfterSuite(alwaysRun = true, description = "Стоп firefox")
    public void tearDown() {
        driver.quit();
    }
}

(Sergey Korol) #11

Человеку нужно разобраться в концепции для начала. А вы ему еще больше кода кидаете.


(Eugene Moskalenko) #12

Дак давайте разбиремся, просто на таких примерах можно показать, что хорошо, а что плохо…


(Шевченко Владислав) #13

Очень распространенное решение, я и сам так делал :wink:


#14

а как теперь делаете?)


(Шевченко Владислав) #15

Делал и делаю) Так же попробую описать еще один интересный подход из практики:
Есть Singleton дли инициализации драйвера (заточен на firefox)

public class WebDriverInstance {
    private static WebDriver ourInstance;

public static   WebDriver getInstance() {
    if (ourInstance == null){
    return ourInstance = Initialize();
    }
    return ourInstance;
}

private WebDriverInstance() {
}

private static WebDriver Initialize() {
    FirefoxProfile profile = new FirefoxProfile();
    profile.setPreference("network.automatic-ntlm-auth.trusted-uris", "xx.com,localhost");
    profile.setPreference("network.ntlm.send-lm-response", true);

    WebDriver driver = new FirefoxDriver(profile);    //get browser
    driver.manage().window().maximize();
    driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
    driver.manage().timeouts().pageLoadTimeout(100, TimeUnit.SECONDS);
    WebDriverRunner.setWebDriver(driver); //SELENIDE
    System.out.println("WebDriverRunner.setWebDriver(driver);");
    Configuration.timeout = 50000;
    return driver;
}

}`

Так же имеется класс EnvironmentConfiguration откуда берется драйвер для дальнейших нужд

`public class EnvironmentConfiguration {
    private boolean disposed = false;
    private String applicationUrl;
    private WebDriver driver;


public  EnvironmentConfiguration(String applicationUrl) {
    this(applicationUrl, WebDriverInstance.getInstance());
}

public EnvironmentConfiguration(String applicationUrl, WebDriver driver) {
    this.applicationUrl = applicationUrl;
    this.driver = driver;
}

}`

Далее идет абстрактный Page в котором собраны общие методы по работе со страницами и в конструкторе создается конфиг, который в свою очередь подтягивает драйвер

`public abstract class Page implements IPage {
        protected EnvironmentConfiguration configuration;
        protected int pageOpenTimeout = 500;
        protected IPage prevPage;
        protected Logger l = Logger.getLogger(Page.class);

public Page(EnvironmentConfiguration configuration) {
    if (configuration == null) {
        throw new IllegalArgumentException("configuration is null");
    }

    this.configuration = configuration;
}`

Потом от этого Page наследуются все страницы. В тесте создается страница и в нее предается конфиг.

Так как я пользовался jbehave то в базовом тестовом классе конфиг поднимался так

`public class TestBase  {
    protected EnvironmentConfiguration config;


@BeforeStory
public void beforeMethod() {
     if(config == null){
    config = new EnvironmentConfiguration(application_url);
     }
   
}`

(Eugene Moskalenko) #16

в общем инициализацию драйвера надо делать исключительно за пределами тестов и объектов страниц или компонентов, но при этом делать это так, чтобы этот самый драйвер юзать не только в BaseScreen, но также можно было его юзать и в других класах… Подходов есть куча разных.

Самая большая проблема, наверное - это то, что в BasePage приходится скапливать все методы по работе с пейджом, зачастую туда ложатся методы ожиданий, методы связанные с кликом и прочими вещами, что не есть хорошо, поскольку лучше разделять по такому принципу:

  • методы по работе с элементами лежат в своем классе
  • методы ожиданий в своем
  • методы которые обобщенные для переиспользования на других всех скринах или большинства - лежать в BasePage

в джаве нет множественного наследования, поэтому ты можешь наследовать только BasePage, в котором будут лежать только общие методы для всех скринов. Методы по работе с элементами уже туда не сложишь. Получается надо либо ухищряться, либо делать каскадное наследование: - UiElements наследуется от WaitingElementsts, WaitingElements в свою очередь наследуется от BasePage в котором реализован Driver и так драйвер переходит в тесты.

На помощь еще могут прийти хелперы, но придется их дергать, что тест делает не очень красивым…

Вот наверное почему driver должен быть изолирован от тестов и страниц, потому что его ты будешь инициализировать в разных класах своего приложения и везде он должен работать…

Даже чтобы сделать скриншот листенером, тебе придется использовать там драйвер… А сами тесты уже будут юзать реализацию с этим драйвером. То есть не придется заботиться о драйвере, потому что это будет делать framework за тебя…

Инициализация драйвера - это же по сути вызов getDriver :slight_smile:


(denys shynkarenko) #17

Сорри, за незапланированный перерыв.

Все таки хотелось бы довести обсуждение до какой то логической точки.

Итак, по моему мнению:
Условие для инициализации драйвера это запуск теста или группы тестов:)
Тестам драйвер вообще не нужен.
Драйвер нужен поидее страницам и тем компонентам, которые со страницами работают, и драйвер возможно туда стоит просто передавать.

Рассуждения более менее верные?


(Sergey Korol) #18

Начнем с того, что драйвер запускает не тесты, а браузер. Тесты запускает unit framework (к примеру, testng / junit). Для драйвера важно не “что”, а “где и когда”. При этом, “где” - в изолированном потокобезопасном контексте. “Когда” - к примеру, перед [suite, test, class, method]. Вот тут вам сразу и задачка - понять, когда лучше всего следует поднимать драйвер.

А зачем страницам нужен драйвер? Страницы - это слой вашего домена, а драйвер - фреймворка. Если вы выносите драйвер на уровень страниц, это ничем не лучше его использования в самих тестах.


(denys shynkarenko) #19

Да, я понимаю, что драйвер запускает не тесты:). Я хотел написать, что условие для инициализации драйвера - запуск тестов. Мы запустили тесты и драйвер должен тоже запуститься. Каким образом пока не важно.

Страницам нужен драйвер, потому что методы страниц с помощью драйвера что-то делают на базовом уровне - методы позволяют работать с элементами страницы и совершать какие-то действия. Каким образом можно минимизировать зависимость страницы от драйвера?


(Sergey Korol) #20

Т.е. если у вас есть 10 страниц, и во всех нужно вводить какие-то значения в input поля, вы в каждой странице будете дублировать driver.findElement(locator).sendKeys(text) по отношению к каждому полю, так?