Паттерны для тестировщиков. Proxy (заместитель)

habr.png

Прежде, чем вот это все


Привет. Я — Дима и я не знаю паттернов. Как для тестировщика, не сказать, что проблематично. Как для автоматизатора…? Ну, давайте честно, жить тоже можно.
Из чатиков, конференций и общения с коллегами, понятно: главный паттерн — PageObject — выучен, чего еще нужно?

А здесь вот был большой такой абзац размышлений о том, почему мы, на самом деле не используем шаблоны проектирования: мы их не знаем или на и без них неплохо? Еще были углубления в историю, что паттерны — это вам не это, а десятилетиями проверенные знания и методики, аргументы за и упоминание известных товарищей, которые против.

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

Еще немножко о формате и поехали


Самих статей/книг/видеокурсов по паттернам вполне себе прилично. И я уверен, вы, без труда, найдете ресурс с грамотным объяснением, примерами на вашем любимом ЯП, юэмэляками и т.д.
Задача этой и последующих статей: рассказать о паттернах тем, кто уже работает с кодом, но не имеет хорошей базы по теории. Так что эти статьи, пожалуй, для меня, в первую очередь.
Ну и, поскольку, сам я — тестировщик, да и в моем круге общения немало таких же, рассказы будут с уклоном на автоматизацию тестирования.
Какие-то из паттернов покажутся странными и не очень полезными. А с какими-то вы, наоборот, обнаружите, что что-то подобное уже давно реализуете, только не знали, что это так называется.
В любом случае, я надеюсь, вы найдете для себя что-то полезное.
И да, я буду очень рад конструктивной критике в комментариях.
Вот.

  • про себя — рассказал
  • что будет — рассказал
  • зачем — рассказал
  • для кого и как — рассказал


Можно начинать.

Proxy — Прокси — Заместитель


Теория (чуть-чуть)


Паттерн прокси (в русскоязычных изданиях, Заместитель). Идея в том, чтобы выдать для работы не реальный объект, а подмену, которая использует методы объекта + нашу логику, если мы такую добавили. Все.
Как это делается:
1. Создаем интерфейс с публичными методами объекта, который хотим подменить
2. Создаем класс, который
— реализует этот интерфейс
— имеет доступ к оригинальному объекту, чтобы вызывать его методы
3. Добавляем в методы созданного класса свою логику

Практика


На практике должно быть понятней.

Дано: Есть у нас WebDriver. И есть у него метод findElements (By by);

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

driver.findElements(By.cssSelector(".item"));

я хочу видеть в логах запись — сколько элементов было найдено

Решение 1. В лоб. Чего уж там: я его применял.
Просто берем и, при каждом вызове пишем, сколько было найдено:


List items = driver.findElements(By.cssSelector(".item"));
logger.info("Found {} items", items.size());


Вариант нормальный, пока таких вызовов, ну, скажем, 7. Хотя уже неприятно и минусы такого подхода очевидны любому, кто хоть раз «слегка модифицировал свой код».

Решение 2. Используем прокси.
WebDriver — это интерфейс. Объявлены методы, но нет реализации. Реализацию содержат ChromeWebDriver, FirefoxWebDriver и т.д.
Нам, в тестах, не обязательно работать с каким-то конкретным классом для хрома или сафари. Нужно только, чтобы класс имплементил интерфейс WebDriver. Это и сделаем:

  1. Создаем интерфейс с публичными методами объекта, который хотим подменить


    В нашем примере, такой интерфейс уже есть — WebDriver.
  2. Создаем класс, который
    — реализует этот интерфейс
    — имеет доступ к оригинальному объекту, чтобы вызывать его методы

    
    public class LoggerWebDriver implements WebDriver{
        private WebDriver driver;
    
        public void get(String s) {     
        }
    
        public List findElements(By by) {
            return null;
        }
    
        public WebElement findElement(By by) {
            return null;
        }
    //остальные методы отрезал для краткости
    }
    
    
  3. Добавляем в методы созданного класса свою логику

    
    public class LoggerWebDriver implements WebDriver {
        private WebDriver driver;
        private final Logger logger = LogManager.getLogger(LoggerWebDriver.class);
    
        LoggerWebDriver() {
            //жестких ограничений по конструктору нет. 
            //В идеале, он(и) должны повторять конструкторы объекта
            this.driver = new ChromeDriver();
        }
    
        public void get(String var1) {
            driver.get(var1);
        }
    
        public List findElements(By var1) {
            List items = driver.findElements(var1);
            logger.info("Selector {}. Found {} elements", var1.toString(), items.size());
            return items;
        }
    
        public WebElement findElement(By var1) {
            return driver.findElement(var1);
        }
    //остальные методы отрезал для краткости
    }
    


Что произошло?
В класс добавлен

private WebDriver driver;


Это тот самый объект, который мы хотим подменить. Именно его методы мы будем вызывать дальше.
Теперь, можно посмотреть, например, на метод

    
public void get(String var1) {
      driver.get(var1);
}

Все, что делает метод — вызывает get () у настоящего драйвера.
А вот метод

    
public List findElements(By var1) {
      List items = driver.findElements(var1);
      logger.info("Selector {}. Found {} elements", var1.toString(), items.size());
      return items;
}

мы расширили в соответствии с нашей задачей.

Вот и все, можно юзать в тестах:


WebDriver driver = new LoggerWebDriver();
driver.get("http://google.com");
List items = driver.findElements(By.cssSelector("a"));
// => Selector By.cssSelector: a. Found 48 elements



Вот. Паттерн хорош, если нужно навесить логирование, кэширование, ленивую инициализацию, контроль доступа к методам. В общем, если вы пишете код и вам очень хочется, чтобы была какая-то прослоечка с вашими фичами, один из вариантов — прокси.

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

© Habrahabr.ru