Прежде, чем вот это все
Привет. Я — Дима и я не знаю паттернов. Как для тестировщика, не сказать, что проблематично. Как для автоматизатора..? Ну, давайте честно, жить тоже можно.
Из чатиков, конференций и общения с коллегами, понятно: главный паттерн — PageObject — выучен, чего еще нужно?
А здесь вот был большой такой абзац размышлений о том, почему мы, на самом деле не используем шаблоны проектирования: мы их не знаем или на и без них неплохо? Еще были углубления в историю, что паттерны — это вам не это, а десятилетиями проверенные знания и методики, аргументы за и упоминание известных товарищей, которые против.
Но, в конечном счете, знание паттернов точно не помешает.
Так что, ни в коем случае не призывая вас к повсеместному вкручиванию шаблонов в код, начинаю публиковать цикл статей по паттернам.
Еще немножко о формате и поехали
Самих статей/книг/видеокурсов по паттернам вполне себе прилично. И я уверен, вы, без труда, найдете ресурс с грамотным объяснением, примерами на вашем любимом ЯП, юэмэляками и т.д.
Задача этой и последующих статей: рассказать о паттернах тем, кто уже работает с кодом, но не имеет хорошей базы по теории. Так что эти статьи, пожалуй, для меня, в первую очередь.
Ну и, поскольку, сам я — тестировщик, да и в моем круге общения немало таких же, рассказы будут с уклоном на автоматизацию тестирования.
Какие-то из паттернов покажутся странными и не очень полезными. А с какими-то вы, наоборот, обнаружите, что что-то подобное уже давно реализуете, только не знали, что это так называется.
В любом случае, я надеюсь, вы найдете для себя что-то полезное.
И да, я буду очень рад конструктивной критике в комментариях.
Вот.
- про себя — рассказал
- что будет — рассказал
- зачем — рассказал
- для кого и как — рассказал
Можно начинать.
Proxy — Прокси — Заместитель
Теория (чуть-чуть)
Паттерн прокси (в русскоязычных изданиях, Заместитель). Идея в том, чтобы выдать для работы не реальный объект, а подмену, которая использует методы объекта + нашу логику, если мы такую добавили. Все.
Как это делается:
1. Создаем интерфейс с публичными методами объекта, который хотим подменить
2. Создаем класс, который
— реализует этот интерфейс
— имеет доступ к оригинальному объекту, чтобы вызывать его методы
3. Добавляем в методы созданного класса свою логику
Практика
На практике должно быть понятней.
Дано: Есть у нас WebDriver. И есть у него метод findElements(By by);
Задача: Мне очень сильно нужно логировать, сколько элементов было найдено по селектору.
Каждый раз, когда я вызываю метод
driver.findElements(By.cssSelector(".item"));
я хочу видеть в логах запись — сколько элементов было найдено
Решение 1. В лоб. Чего уж там: я его применял.
Просто берем и, при каждом вызове пишем, сколько было найдено:
List<WebElement> items = driver.findElements(By.cssSelector(".item"));
logger.info("Found {} items", items.size());
Вариант нормальный, пока таких вызовов, ну, скажем, 7. Хотя уже неприятно и минусы такого подхода очевидны любому, кто хоть раз «слегка модифицировал свой код».
Решение 2. Используем прокси.
WebDriver — это интерфейс. Объявлены методы, но нет реализации. Реализацию содержат ChromeWebDriver, FirefoxWebDriver и т.д.
Нам, в тестах, не обязательно работать с каким-то конкретным классом для хрома или сафари. Нужно только, чтобы класс имплементил интерфейс WebDriver. Это и сделаем:
Создаем интерфейс с публичными методами объекта, который хотим подменить
В нашем примере, такой интерфейс уже есть — WebDriver.
Создаем класс, который
— реализует этот интерфейс
— имеет доступ к оригинальному объекту, чтобы вызывать его методыpublic class LoggerWebDriver implements WebDriver{ private WebDriver driver; public void get(String s) { } public List<WebElement> findElements(By by) { return null; } public WebElement findElement(By by) { return null; } //остальные методы отрезал для краткости }
Добавляем в методы созданного класса свою логику
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<WebElement> findElements(By var1) { List<WebElement> 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<WebElement> findElements(By var1) {
List<WebElement> 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<WebElement> items = driver.findElements(By.cssSelector("a"));
// => Selector By.cssSelector: a. Found 48 elements
Вот. Паттерн хорош, если нужно навесить логирование, кэширование, ленивую инициализацию, контроль доступа к методам. В общем, если вы пишете код и вам очень хочется, чтобы была какая-то прослоечка с вашими фичами, один из вариантов — прокси.
Теперь, когда мы знаем на один паттерн больше, напомню, что когда в руке молоток, все вокруг кажется гвоздями. Просто помните об этом.
Комментарии (4)
lxsmkv
17.06.2018 16:08Да, автоматизатору паттерны не помешают. Было недавно дело, у меня приложение имеет две инкарнации, соответственно 2 варианта графического интерфейса. И вот стал я думать как на pageоbject фасад натянуть. Поднатужился — натянул. Смотрю — очень весело стало жить. Новые тесты писать перестались, а все время на создание совместимости кода для двух интерфейсов уходит. Задумался, а не фигню ли я делаю? И понял, что и вправду фигню. И разделил я тогда код на два проекта, и все встало на свои места.
Nidhognit
Хорошая статья, но больше похоже что вы используете патерн Декоратор
EreminD Автор
спасибо за ответ
я, перед публикацией, тоже задумался, не декоратор ли?
Поискав различия, пришел к тому