Как правильно выбрать сделать, чтобы исключить дублирование кода?

Как правильно выбрать сделать, чтобы исключить дублирование кода?

тесты пишутся под Java c Webdrver2

Задача: пройтись по определенным страницам доступные только залогиненым пользователям (Wishlist, Checkout, страницы в My Account и др.) и сделать их скриншоты.

 
По тех страницах, которые доступны не залогиненым пользователям - нет проблем, так как использую один класс, читаю список URL-ов из файла обхожу в цикле и делаю скрины . Но вот как только использую Webdriver для входа пользователя возникает проблема, так как для каждой страницы использую отдельный класс. Можно конечно это все замутить в одном классе. Но хочется красиво и думаю это будет часто встречаться в дальнейшем.
 
1 вариант: пишу тест для снятия скиншота для страницы Wishlist, где использую инициализацию драйвера:
 
        profile.addExtension(new File(ff_extension));
        driver = new FirefoxDriver(new FirefoxBinary(new File(ff_path)), profile);
 
и еще функцию снятия скриншота, и еще всякие переменные. И так должен делать для каждой страницы: Wishlist, MyOrders, MyAddress и др. Код повторяется. При смене переменных, функций, инициализаций - большая вероятность не до смотреть, не все поменять, короче - геморрой.
 
2 вариант: создаю абстрактный класс Base в котором описываю все повторяющиеся функции, переменные, т.е. избегаю дублирование кода и как следствие - уменьшение риска возникновения ошибок. Создаю класс Login - наследую от Base. Создаю класс Wishlist - наследуя от Login. Создаю класс MyOrders - наследуя от Login. В классе RunTests создаю:
        WishlistPage wList = new WishlistPage();
который наследует все методы от Base и Login, а вот медоды из класса MyOrders - не наследуются.
Конечно есть выход: Login - наследовать от Base, Login от Base, Wishlist от Login, MyOrders от Wishlist, MyAddress от MyOrders....
и в классе RunTests создавать:
        MyAddress myAddr = new MyAddress();
и уже в нем все методы наследованы. Но это как-то не комильфо.
Как вы поступаете в таком случае. Я думаю такие примеры возникают у каждого и часто. Хочется писать не как прийдется, а как правильно.

1) Используем TestNG DataProvider для передачи линков.

2) Создаем 2 теста, и соответственно 2 провайдера, последовательно / параллельно передающих ссылки для "простых" и требующих авторизации пейджей.

3) Создаем аннотацию @AuthRequired. Помечаем ею тест, принимающий ссылки страниц, требующих авторизации.

4) Все пейджы наследуют BasePage - обертку для методов селениума.

5) Все тесты наследуют BaseTest - инициализатор драйвера.

6) В @BeforeClass / Method / etc. инитим драйвер и проверяем наличие аннотации @AuthRequired. Если true, проводим авторизацию; если false, просто идем на первую страницу сайта.

7) В тесте принимаем линк от провайдера, по которому нужен редирект. С учетом того, что мы уже авторизовались, драйвер без проблем отправит нас на нужную страницу вызовом навигейта или гета. В идеале это дело можно вызвать из того же LoginPage, обратившись к методу обертки.

8) В @AfterClass / Method / etc. делаем скриншот до убиения драйвера.

Итого, у нас есть 2 теста, содержащих по 1-2 строки и 2 провайдера с массивами линков - еще 10 строк кода.

почитал про TestNG DataProvider - это хранилище (линков - в моем случае), хотя я беру из текстового файла (не принципиально, я думаю).

а вот остально интересно, но не совсем понятно. Если можно пример/схемку

а авторизация необходима перед проверкой каждого урла?

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

то авторизация зачастую больше не запрашивается, только если выйдешь из браузера

поэтому в данном случае можно авторизироваться всего лишь один раз и вообще непариться

 

а вообще второй вариант, который Вы предложиили - направление верное, но решение выглядит очень запутанным, надо упрощать

ArtOfLife предложил хорошее решение, только Вам надо будет погуглить немного чтобы фактически написать этот код

 

можно сделать немного проще, чтобы не писать кучу тестов

пишем один метод openPage в абстактном классе,

все старницы открывает через метод openPage

метод openPage проверят, являемся ли мы сейчас на сранице авторизации или нет

если да, то логинемся и возвращаем нужную странцу, если же не на странице авторизации, то просто передаем управление дальше

спасибо. Буду пробовать и вариант ArtOfLife и Ваш вариант. 

Что-то по типу такого:

@Retention(value = RUNTIME)
@Target(value = METHOD)
public @interface AuthRequired {}
 
    @BeforeMethod
    public void beforeMethod(Method method) {
        initDriver();
 
        if (method.isAnnotationPresent(AuthRequired.class)) {
                user = provideDefaultUser();
                login(user);
        } 
    }
 
    @Test(dataProvider= "dataProvider")
    @AuthRequired
    public void checkPages(String link) {
        redirectToPage(link);
    }

    @AfterMethod

    public void afterMethod() {
        makeScreenshot();
        closeDriver();
    }
По поводу схемы с BasePage - тут многое зависит от вашей архитектуры. Но суть в создании обертки для методов селениума. Естественно, обертка должна видеть ваш драйвер.
Часть выше приведенного кода будет помещена в некий BaseTest, который будут наследовать все ваши тесты.