Программирование для автоматизации Flashcards

1
Q

Martin Fowler about PageObject

A

Page objects are a classic example of encapsulation - they hide the details of the UI structure and widgetry from other components (the tests).

A page object wraps an HTML page, or fragment, with an application-specific API, allowing you to manipulate page elements without digging around in the HTML.

The basic rule of thumb for a page object is that it should allow a software client to do anything and see anything that a human can.

It should also provide an interface that’s easy to program to and hides the underlying widgetry in the window.

  1. So to access a text field you should have accessor methods that take and return a string,
  2. check boxes should use booleans,
  3. and buttons should be represented by action oriented method names.

The rule of thumb is to model the structure in the page that makes sense to the user of the application.

If you navigate to another page, the initial page object should return another page object for the new page

In general page object operations should return fundamental types (strings, dates) or other page objects.

There are differences of opinion on whether page objects should include assertions themselves, or just provide data for test scripts to do the assertions.

  • Advocates of including assertions in page objects say that this helps avoid duplication of assertions in test scripts, makes it easier to provide better error messages, and supports a more TellDontAsk style API.
  • Advocates of assertion-free page objects say that including assertions mixes the responsibilities of providing access to page data with assertion logic, and leads to a bloated page object.

I favor having no assertions in page objects. I think you can avoid duplication by providing assertion libraries for common assertions - which can also make it easier to provide good diagnostics.

Page objects are commonly used for testing, but should not make assertions themselves. Their responsibility is to provide access to the state of the underlying page. It’s up to test clients to carry out the assertion logic.

It’s common to write tests using some form of DomainSpecificLanguage, such as Cucumber or an internal DSL. If you do this it’s best to layer the testing DSL over the page objects so that you have a parser that translates DSL statements into calls on the page object.

Patterns that aim to move logic out of UI elements (such as Presentation Model, Supervising Controller, and Passive View) make it less useful to test through the UI and thus reduce the need for page objects.

Page objects are a classic example of encapsulation - they hide the details of the UI structure and widgetry from other components (the tests). It’s a good design principle to look for situations like this as you develop - ask yourself “how can I hide some details from the rest of the software?” As with any encapsulation this yields two benefits. I’ve already stressed that by confining logic that manipulates the UI to a single place you can modify it there without affecting other components in the system. A consequential benefit is that it makes the client (test) code easier to understand because the logic there is about the intention of the test and not cluttered by UI details.

https://martinfowler.com/bliki/PageObject.html

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Когда нужно использовать классы, а когда функции?

A

Если класс инициализируется один раз и затем используется всего 1 раз - это должна быть функция

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Zen of python

A
  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Sparse is better than dense.
  • Readability counts.
  • Special cases aren’t special enough to break the rules.
  • Although practicality beats purity.
  • Errors should never pass silently.
  • Unless explicitly silenced.
  • In the face of ambiguity, refuse the temptation to guess.
  • There should be one– and preferably only one –obvious way to do it.
  • Although that way may not be obvious at first unless you’re Dutch.
  • Now is better than never.
  • Although never is often better than right now.
  • If the implementation is hard to explain, it’s a bad idea.
  • If the implementation is easy to explain, it may be a good idea.
  • Namespaces are one honking great idea – let’s do more of those!
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Языки программирования для автоматизации

A
  1. Python - мощный язык, умеренно простой, легко писать скрипты любой сложности. Много различных библиотек для работы со всем, чем угодно. По факту 1 тест ранер - pytest.
  2. JavaScript/Typescript - язык фронтенда и на данный момент номер 1 по популярности. Безмерное количество разных библиотек. Выбирая JS/TS, вы увеличиваете шансы приблизиться к Shift left и начать делать тестирование фронтеда правильно. За последние годы появилось много библиотек: Jest, Cypress, Puppeteer, WebdriverIO. Недостаток - асинхронность, которая в тестах не нужна. Вторым недостатком являются странности языка по части работы некоторых функций.
  3. Старуха Java - классика. Почему java так популярна в мире автоматизации? Потому что до JavaScript эры именно Java была самым популярным языком. Selenium был написан для джавы и активнее всего развивался имеенно под эту экосистему. Материалов, лекций и документации больше всего именно под Java + Selenium. На стороне Java надежность, стабильность и наличие кучи библиотек. Большинство вакансий для QA Automation пока еще требуют знаний java. Есть и недостаток - громоздкость и сложность. Тесты - это то, что нужно уметь быстро создавать, быстро чинить и быстро удалять. Быстро создать тесты у вас получится, только если вы годами на ней пишете. Поставить Java, поставить Maven/Gradle, написать build script, настроить junit/testng, скомпилировать - это все занимает в разы больше времени, чем в питоне или Js. Kotlin? Он ничего принципиально не меняет.
  4. C# - есть достаточно большое количество проектов, написанных на C#/.Net. Этот язык не очень популярен в мире автоматизации, там есть 3-5 библиотек, которые покроют вам нужды автоматизации. Скажу честно, мне этот язык не нравится чисто по каким-то субьективным особенностям. Из недостаков - завязка на технологии и экосистему Майкрософт и Windows.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Playwright vs Selene

A

Плюсы playwright:

  • Плейврайт использует протокол управления браузером на основе websocket а селениум - на основе http. Селениум что-то там питался тоже реализовать свой на основе websocket, но пока глухо…
  • Соответственно плюсы те же что и у вебсокетов по сравнению с http - двунаправленная полнодуплексная передача сообщений.
  • Селениум вынужден “опрашивать извне по http браузер в цикле при ожиданиях” а плейврайт опрашивает прямо на стороне браузера напрямую без затрат времени на передачу сообщений по http, и когда “ожидание закончилось” он просто маякует клиенту о конце.
  • Это банально оптимальней по времени. По этому плейврайт в среднем на процентов 40 быстрее чем селен например… как раз изза того что ожидания в плейврайте работают эффективней…
  • Кроме этого можно на плейврайте делать то что нельзя на селениуме, например подключится к живому уже открытому браузеру…

С другой стороны селениум можно для мобилок и даже десктоп использовать.

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Best automation practices

A

https://docs.cypress.io/guides/references/best-practices

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Рекомендации и общепринятые договоренности в подборе имен (Python)

A

Исправить и дополнить. Прочитать на свежую голову

Именование любых сущностей в программировании – очень важно. От того как ты будешь называть свои модули и функции, переменные, классы, поля и методы – зависит понимание твоего кода другими программистами.

  • «сложное для восприятия имя» будет замедлять работу с кодом,
  • «имя приводящее к неправильному представлению о именованной сущности» рано или поздно приведет к появлению дефектов.
  • Также, обычно важна «общепринятая в кругах программистов» стилистика, которой стоит следовать чтобы «поставлять как можно более привычный а следовательно и проще понимаемый большинству код»
    • Рекомендуется нарушать любую такую общепринятую конвенцию только хорошо подумав, и поняв что плюсов от «нарушения» – больше чем минусов (например минусом может быть – неочевидность и дикость для других членов команды).

Начиная работать в новом для себя проекте, поинтересуйся о договоренностях в нейминге, принятых в конкретно этом проекте. Возможно, есть документ, описывающий это. Возможно, тебе придется сделать выводы самостоятельно, проанализировав уже существующий и использующийся код. В любом случае – стоит придерживаться «локальных» правил – так как для всей команды они уже очевидны.

Имя модуля?

  • Отражает контекст использования функций, переменных, либо классов в этом модуле
  • не содержит нумераций и прочей структурной информации
    • нумерации и организационное структурирование лучше реализовывать «пакетами»
  • начинается с маленькой буквы
    • shortlowercase
      • longer_underscored_is_ok_if_improves_readability
        ### Имя класса?
  • Отражает сущность которую представляют объекты этого класса
    • Обычно характер сущности определяется – «поведениями обьекта» – представленными в виде его публичных методов
  • не содержит нумераций и прочей структурной информации
    • нумерации и организационное структурирование лучше реализовывать «пакетами» (python packages), а не уточнять в имени модуля либо класса
  • начинается с большой буквы

Имя пакета python?

  • отражает структурную информацию о коде внутри пакета, который по сути группирует python-модули «по контексту»
    • shortlowercase
      • long_underscored_is_discouraged

Имя функции либо метода?

  • отражает/описывает цель которую выполняет функция или метод
  • начинается с маленькой буквы

Имя переменной?

  • отражает/описывает то, «что сохраняет» переменная
  • начинается с маленькой буквы

Имя константы?

  • SCREAMING_SNAKE_CASE
    • большие буквы
    • слова разделены подчеркиванием
      ### Имя тест-модуля или тест-класса?
  • Имя тест-модуля либо тест-класса играет роль имени тест-суита (набора тест-кейсов) и должно отражать в общем – что тестируют методы данного тест-класса. При этом учитывая соглашения о именах для модулей и классов в Python.
    • формат для имени тест-модуля
      • test_<[имя_приложения][_часть_приложения][_фича_приложения]>.py
      • или
      • <[имя_приложения][_часть_приложения][_фича_приложения]>_test.py
    • формат для имени тест-класса
      • Test<[ИмяПриложения][ЧастьПриложения][ФичаПриложения]>
        • пример хорошего имени:class TestSomeSocialNetworkPostManagement: ...
  • При этом стоит учитывать контекст, и по возможности подбирать более лаконичные имена тест-классов.
  • Обычно имя тест-класса начинают с Test, чтобы указывать, что это именно тест-класс а не обычный
    • Часто, по умолчанию тест-раннеры настроены по умолчанию таким образом, что будут искать и запускать только те тесты, которые начинаются на Test (хотя это можно перенастроить).
    • Иногда этой рекомендации не следуют, например когда симулируютBDD, где файлы со сценариями обычно называют «фичами» (features) или «спеками» (specs от specifications) – и тогда либо никакой приставки не пишут, либо используютSpecвместоTest.
    • Рекомендуется нарушать любую общепринятую конвенцию только хорошо подумав, и поняв что плюсов от «нарушения» – больше чем минусов (например минусом может быть – неочевидность и дикость для других членов команды…)
  • В имя тест-класса не нужно выносить структурную информацию (номер версии, вариант решения и т. п.) или свойства, которые не характеризуют тест-класс в целом с точки зрения тестового покрытия
    • лучше для этого использовать отдельные пакеты

Пакеты в тестовом проекте?

  • С помощью пакетов можно фиксировать любую структурную информацию, например
    • если много тестов в разных стилях… Например, если часть тестов атомарные (с фокусом на одной фиче), а часть – стиля «end to end», покрывающие несколько фич в контексте определенного «пути пользователя» («user workflow», «user journey»), то можно сгрупировать тесты по следующим пакетам:
      • пример 1:
        • some-social-network-test/tests/e2e
        • some-social-network-test/tests/atomic
      • пример 2:
        • some-social-network-test/tests/workflows
        • some-social-network-test/tests/features
      • пример 3:
        • some-social-network-test/tests/journeys
        • some-social-network-test/tests/features

Тест-функции и тест-методы?

В первую очередь, имя тест-функции либо тест-метода играет роль имени тест-кейса и отображает то, что покрывают его шаги. Желательно, чтобы оно отвечалостандартным договоренностям Python по составлению имен функций или методов… Тогда его можно строить по одной из схем:

  • test_<фича | под_фича>_[_]<ожидаемый_ввод | тестовое_состояние>_[_]<ожидаемое_поведение>

Например:

test_register_new_user_existing_email_given_should_show_error_message()

либо чуть более выделяя структуру:

test_register_new_user\_\_existing_email_given\_\_should_show_error_message()

либо, хорошо подумав, все взвесив, и приняв решение пойти против стандартных соглашений в Python:

test_registerNewUser_ExistingEmailGiven_ShouldShowErrorMessage()

… аргументируя свое решение компактностью и тем, что зажимать шифт при слепом наборе проще чем тянуться правым мизинцем к подчеркиванию с зажатым левым мизинцем шифтом ;p

  • test_<фича | под_фича>_[_]<ожидаемое_поведение>_[_]<ожидаемый_ввод | тестовое_состояние>

Примеры:

test_register_new_user_shows_error_message_when_email_exists()

test_register_new_user\_\_shows_error_message\_\_when_email_exists()

test_registerNewUser_ShowsErrorMessage_WhenEmailExists()

  • given_<предусловия>_when_<ожидаемыеДействия>_then_<ожидаемое_поведение>

Для такого формата нужны будут дополнительные настройки тест-раннера, например для pytest:

content of pytest.ini in the root of your tests-project
[pytest]
python_functions = given_*

Примеры:

given_email_exists_when_register_new_user_then_error_message_is_shown()

given_email_exists\_\_when_register_new_user\_\_then_error_message_is_shown()

GIVEN_email_exists_WHEN_register_new_user_THEN_error_message_is_shown()

givenEmailExists_whenRegisterNewUser_thenErrorMessageIsShown()

Как-то длинновато получается? – Главное, чтобы было читабельно. Лаконичность это прекрасно, но только если не ухудшает читабельности;).

https://autotest.how/python/naming-guidelines-ru

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Docstring

A

Elevator pitch - what it is you’re doing

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

init в классе

A

Init isn’t a constructor. It’s job is to initialize the instance variables

Self - is an instance, it’s already been made when init is called

Init - is initializer. It takes existing instance self and populates it

Goal of instance variables - get the data you need into instance
Goal of init - populate that instance

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Синтаксис классов в python

A
import math

class Circle(object):

    version = '0.4'

    def \_\_init\_\_(self, radius):
        self.radius = radius

    def area(self):
		    return math.py * self.radius ** 2.0
				
    def perimeter(self):
		    return 2.0 * math.pi * self.radius
    
    @classmethod
    def from bbd(cls, bbd):
		    'Construct a circle from a bounding box diagonal'
		    radius = bbd / 2.0 / math.sqrt(2.0)
		    return cls(radius)
		
		@staticmethod
    def angle_to_grade(self, angle):
		    'Convert angle in degreee to a percentage grade'
		    return math.tan(math.radians(angle)) * 100.0
		
    class Tire(Circle):
		    def perimeter(self):
				    return Circle.perimeter(self) * 1.25
  1. Cначала создаётся экземпляр класса (self)
  2. Затем вызывается init и он наполняет ими экземпляр класса self
  3. В методе - аргументом передаётся self
  4. 3.14 - заменяется переменной
  5. Версия указывается как переменная класса. Версии пишутся через str, иначе будет сбоить
  6. Суть class variable - to share data. Shared data stored at class level
  7. В питоне атрибуты доступны для изменения пользователями - python way (в java и c++ это не допустимо)
  8. Подкласс не использует инит, т.к. наследует его от родителя
  9. Class methods create alternative constructors (solves constructor wars).
    1. В случае войны конструкторов - все должны выйграть, для каждого создаются альтернативные конструкторы через @classmethod. И у всех дочерних классов должен быть к ним доступ - для этого возвращается cls(radius), он поддерживает дочерние классы.
  10. angle_to_grade - функция которая относится к кругу и не должна использоваться сферами или другими классами. Поэтому кладётся внутрь класса. Но, чтобы не создавать объект, а все равно иметь возможность использовать её - она создается как @staticmethod. Ей не нужно ничего знать про инстанс круга.
  11. Цель staticmethod - attach functions to classes. Делается, чтобы проще было найти функцию и чтобы люди использовали функцию в подходящем контексте
  12. \_\_perimeter
  13. @property -

Flyweight design pattern: Slots
* supresses the instance dictionary
* makes class lightweight
* Делается в самом конце при значительном масштабировании, т.к. иначе теряется возможность inspect the dictionary, добавлять новые переменные на ходу
* Делая в конце - достигается эффективность использования памяти

  1. Inherit from object()
  2. Instance variables for information unique to an instance
  3. Class variables - for data shared among all instances
  4. Regular methods - need self to operate on instance data
  5. Class methods - implement alternative constructors. They need cls so they can create subclass instances as well
  6. Static methods - attach functions to classes. They don’t need either self or cls. Static methods improve discoverability and require context to be specified
  7. A property lets getter and setter methods be invoked automatically by attribute access. This allows Python classes to freely expose their instance variables.
  8. The \_\_slots\_\_ variable implements the Flyweight Design Pattern by supressing instance dictionaries

https://youtu.be/HTLu2DFOdTg

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Пирамида тестирования - martinfowler.com

A

Still, due to its simplicity the essence of the test pyramid serves as a good rule of thumb when it comes to establishing your own test suite. Your best bet is to remember two things from Cohn’s original test pyramid:

  • Write tests with different granularity
  • The more high-level you get the fewer tests you should have

Stick to the pyramid shape to come up with a healthy, fast and maintainable test suite: Write lots of small and fast unit tests. Write some more coarse-grained tests and very few high-level tests that test your application from end to end. Watch out that you don’t end up with a test ice-cream cone that will be a nightmare to maintain and takes way too long to run.

In the days of single page application frameworks like react, angular, ember.js and others it becomes apparent that UI tests don’t have to be on the highest level of your pyramid - you’re perfectly able to unit test your UI in all of these frameworks.

https://martinfowler.com/articles/practical-test-pyramid.html

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Комментарии - использовать или нет?

A

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

Поэтому все действия, все названия переменных, названия селектором - должны нести в себе смысл.

Неясные селекторы заворачиваются в осмысленно названные переменные

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Page Object с ассертами или без

A
  • Advocates of including assertions in page objects say that this helps avoid duplication of assertions in test scripts, makes it easier to provide better error messages, and supports a more TellDontAsk style API.
  • Advocates of assertion-free page objects say that including assertions mixes the responsibilities of providing access to page data with assertion logic, and leads to a bloated page object.

Мартин Фаулер: I favor having no assertions in page objects. I think you can avoid duplication by providing assertion libraries for common assertions - which can also make it easier to provide good diagnostics.

Яков Крамаренко:
**В большинстве задач стоящих перед автоматизаторами - тесты которые нужно писать, это не те тесты, где стоит оставлять ассерты снаружи. Это больше подходит для юнит-тестов в большинстве случаев.

На UI - не удобно, хоть и соответствует основным принципам.**

  • Special cases aren’t special enough to break the rules.
  • Although practicality beats purity.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Когда не нужна автоматизация?

A
  1. Цикл жизни проекта крайне мал (неделю на написать и отдать навсегда)
  2. Функциональность находится в активной фазе разработки и требования постоянно меняются
  3. Трудозатраты на разработку автотестов несоизмеримы по времени с ручными проверками (часто происходит с UI)
  4. Мы чётко не можем сформулировать, какая от нее будет польза (автотесты ради автотестов - не работает)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Когда нужна автоматизация?

A
  • Большое количество тестов на разных окружениях и платформах
  • Быстрое предоставление разработчикам информации о состоянии системы
  1. Тестовые сценарии требуется проверять часто, да еще и на нескольких окружениях (браузерах, ОС) или устройствах (100-150 кейсов на 10 мобильных устройствах, а новые версии выкладываются пару раз в неделю)
    1. По другому: полный прогон всего набора тестов руками занимает слишком много времени
  2. Система под тестированием не имеет визуальной части или она еще не была сделана
  3. Необходимо проверять совместимость системы при переходе от версии к версии
  4. Есть большие требования по нагрузке и требуется провести тестирование производительности
  5. Хочется регулярных проверок работоспособности системы
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Всегда ли нужна автоматизация?

A

Не всегда

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Цикл жизни автотеста

A
  1. А нужно ли автоматизировать данный кейс?
  2. Проработка архитектуры
  3. Написание кода
  4. Отладка и проверка теста
  5. Описание и выкладка в репозиторий
  6. Проверка в “бою”
  7. Поддержка
  8. Удаление, если более не актуален
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

Циклы по словарю

Python

A

ключи

Это цикл по ключам

d = {
    "first": 1,
    "second": 2,
    "third": 3
}

for item in d:
    pprint(item)

Output:
'first'
'second'
'third'
for item in d.keys():
    pprint(item)

значения
for item in d.values():
    pprint(item)

пара ключ+значение
for item in d.items():
    pprint(item)

парсить ключи и значения в отдельные переменные сразу
for key, value in d.items():
    print(f"Ключ: {key}, Значение: {value}")
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

Получение значения из словаря через функции

A
users = [
    {"name": "Oleg", "age": 32},
    {"name": "Sergey", "age": 24},
    {"name": "Stanislav", "age": 15},
    {"name": "Olga", "age": 45},
    {"name": "Maria", "age": 18},
]

def get_age(user): # получает и возвращает возвраст
    return user["age"]

users.sort(key=get_age) # сортировка при помощи функции возвращающей возраст

users.sort(key=lambda user: user["age"]) # аналогично тому, что выше, но через лямбда функцию 

users.sort(key=itemgetter("age")) # аналогично, но через питоновскую функцию, которая возвращает функцию как get_age
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Путь к файлу python из которого запущен скрипт

A

os.path.abspath(\_\_file\_\_)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

Абсолютный путь к файлу в текущей папке

A

os.path.abspath("download_file.py")

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

Определение пути к папке в которой лежит файл

A
current_file = os.path.abspath(\_\_file\_\_)

current_dir = os.path.dirname(current_file)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

Склеивание путей независимо от ОС

A
tmp_dir = os.path.join(current_dir, "tmp")
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

Как должна называться папка с проектом автотестов?

A

Название должно отражать суть. Т.е. имя сайта + какие тесты

Например: demowebshop_ui_tests

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q

Можно ли в локаторах завязываться на текст?

A

Не желательно, т.к. может легко поменяться. Можно только в крайних случаях.
Лучше на id или атрибуты типа value или xpath

26
Q

Модульная парадигма программирования

A

Код организовывается через функции и модули

27
Q

Названия классов

A

По тому - какая бизнес логика проверяется. Например - RegistrationPage

28
Q

Шаги рефакторинга тестов в PageObject

A
  1. Все технические части выносятся в функции и переносятся в корневой пакет
  2. Создаётся класс для страницы прямо в тесте
  3. Имя класса - по имени бизнес логики (RegistrationPage)
  4. Создаём метод (например open)
  5. Переносим в него нужный код (browser.open(…))
  6. В тесте создаём экземпляр класса - registration_page = RegistrationPage()
  7. Используем созданный метод simple_registration_page.open()
  8. Далее - пишем имя объекта, имя нового метода, указываем передаваемые данные, Alt+Enter - Add Method - создаём метод в классе через IDE
  9. Затем переносим нужный код в новый метод
  • Сначала для каждого действия создаём по методу
  • Затем уже исходя из необходимой подробности они оставляются в теле теста или переносятся внутрь основных шагов и преобразуются в приватные инкапсулированные методы
  • В атрибуты init переносим только повторяющиеся элементы:
    • self.full_name = browser.element('#userName')
    • self.full_name.type(value)

Оформление проверок

  • Assertion free PageObject - Баз базов, классика ООП. Прячем элемент таблицы в init
    def \_\_init\_\_(self):
        self.registered_user_data = browser.all('#output p')
simple_registration_page.registered_user_data.should(
        have.texts(
            f'Name:{full_name}',
            f'Email:{email}',
            f'Current Address :{current_address}',
            f'Permananet Address :{permanent_address}',
        )
    )
  • Более удобно - скрывать ассерты внутри класса
29
Q

Шаги написания PageObject и тестов для страницы

A
  1. Создаём в корневом пакете страницу
  2. Создаём класс
  3. Оформляем методы для каждого элемента
  4. Повторяющиеся элементы записываем в init
  5. Пишем тест
30
Q

Популярные названия для названий методов проверок в автоматизации

A
  1. assert - больше подходит для юнит тестов
  2. should - высокоуровневый, уровень бизнеса

Не рекомендуется использовать имена из мануального тестирования:

  • verify
  • check

Используются обычно те, о которых договорились на проекте

31
Q

@property в контексте автотестов

A

Метод, который выглядит как переменная

@property
    def registered_user_data(self):
        return browser.all('#output p')
  • Без него - надо указывать скобки: simple_registration_page.registered_user_data().should
  • С ним - можно не укзаывать simple_registration_page.registered_user_data.should

Но, можно просто убрать в init:

    def \_\_init\_\_(self):
        self.registered_user_data = browser.all('#output p')
32
Q

Оформление пользователей через @dataclass

A

data/users.py

@dataclass
class User:
    full_name: str
    email: str
    current_address: str
    permanent_address: str

simple_user = User(
    full_name='Ellend Venture',
    email='ellend@venture.com',
    current_address='Keep Venture, Luthadel',
    permanent_address='Keep Venture, Luthadel',
)

В тестах

from demoqa_ui_tests.test_data.users import simple_user
registration_page.should_have_registered_user_data(
        simple_user.full_name,
        simple_user.email,
        simple_user.current_address,
        simple_user.permanent_address,
    )
33
Q

Application manager

A

PageObject для PageObject-ов. Позволяет иметь одну точку входа, например

  • app.web.login()
  • app.mobile.login()
  • Может содержать методы

В корневом каталоге создаётся файл application.py

В инит создаются все объекты для страниц

class Application:
    def \_\_init\_\_(self):
        self.starting_page = StartingPage()
        self.login_page = LoginPage()
        self.main_page = MainPage()
        self.mobile_login_page = MobileLoginPage()
        self.mobile_main_page = MobileMainPage()

app = Application()
34
Q

Проблемы BasePage

A
  • Иногда в него кладут footer, header и другие панели со всех страниц
  • И каждую страницу наследуют от BasePage
class SimpleUserRegistrationPage(BasePage)
    ...

Все программисты будут высмеивать такое

  • Нарушает принцип Composition Overinheritance
  • Application manager - пример композиции
  • Для понимания к какой категории относить - задать себе вопрос: в каком отношении состоят боковая панель и приложение?
    • Панель является страницей?
    • Приложение имеет панель и Приложение имеет форму для регистрации
    • Но нельзя сказать, что форма регистрации является панелью
    • Это как сказать, что Яша является телефоном - Яша не является телефоном, у Яши есть телефон
    • Если Яша не является телефоном, значит нельзя Яшу наследовать от телефона
35
Q

Куда складываются header, footer и side panel в PageObject?

A

Папка components с соответствующим названием:

  • side_panel.py
  • header.py
  • footer.py
36
Q

Composition Overinheritance

A
37
Q

Синтаксис наследования

A
# define a superclass
class super_class:
    # attributes and method definition

inheritance
class sub_class(super_class):
    # attributes and method of super_class
    # attributes and method of sub_class
38
Q

Allure - что делать, когда тесты локально запускаются нормально, а удалённо - падают?

A
  1. В Allure отчёте добавляются скриншоты для каждого действия
39
Q

Тэги в allure

A

Вариант для программирования - т.к. можно писать условия при выполнении которых ставятся определенные тэги
def test_dynamic_labels():
allure.dynamic.tag(“web”)
allure.dynamic.severity(Severity.BLOCKER)
allure.dynamic.feature(“Задачи в репозитории”)
allure.dynamic.story(“Неавторизованный пользователь не может создать задачу в репозитории”)
allure.dynamic.link(“https://github.com”, name=”Testing”)
pass

Вариант для тестирования (Ерошенко рекомендует его)
@allure.tag(“web”)
@allure.severity(Severity.CRITICAL)
@allure.label(“owner”, “eroshenkoam”)
@allure.feature(“Задачи в репозитории”)
@allure.story(“Авторизованный пользователь может создать задачу в репозитории”)
@allure.link(“https://github.com”, name=”Testing”)
def test_decorator_labels():
pass

40
Q

Активация виртуального окружения python

A

python3 -m venv .venv

source .venv/bin/activate

41
Q

Получение отчёта Allure

A

allure serve - если папка в корне

42
Q

Консистентность в именах

A

Не плодите имен. Бывает, что одну и ту же вещь называют двумя-тремя-четырьмя разными способами. Например, переменная окружения, ее имя в коде, ее имя в логе, имя ключа в JSON, глобальное имя в JS и так далее:

resourcePrefix = getenv(“RESOURCE_PREFIX”)
log.info(“resource prefix: “ + resourcePrefix)
JSON payload = { “resource_prefix”: resourcePrefix }

Они все похожи, но разные. Оправдывается это тем, что в разных языках/окружениях приняты разные соглашения и надо соответствовать. Это аргумент валидный, но исключительно эстетический. Программист объективно видит все возможные стили именования каждый день, так что не будем делать вид, что какое-то конкретное написание его испугает.

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

Второе это распутывание клубка. Вы изучаете код, отлаживаете, просто пишете что-то, и вот вам понадобилась переменная. Вы знаете или легко находите, допустим, как она называлась в системном окружении, но нужна она вам в браузере, и начинается пляска с поисками, что куда кто передал и как переименовал. Во время отладки наоборот: вы знаете конечное имя и вам надо отследить, откуда оно пришло. Мучительная и бесполезная работа. Muda.

Наконец, третье. Процесс переименования сам по себе бесполезен. Он занимает реальные строчки кода, вводит новые сущности. Даже так: вводит новые имена, что когнитивно дороже, чем сущности. Тут работает более общее правило: любое усложнение, любое действие, любое решение — у всего должна быть причина. Конкретная причина, конкретное объяснение, вербальное, формулируемое. Очень легко проверить — просто сформулируйте словами. Здесь же действие налицо, а причина очень зыбкая, почти эфемерная.

Императив достаточно простой: используйте одно и то же имя. Тащите в код переменную окружения COMPANY_RESOURCE_PREFIX? Так и назовите: String COMPANY_RESOURCE_PREFIX = …;. И в JSON засуньте под тем же именем. И в БД. Так же и со всем остальным: имя таблицы и имя ORM-класса. Имя файла и имя переменной с его содержимым. Ключ в хэше и ключ в БД. Это упрощение, а упрощения окупаются многократно.

Да, это не всегда это возможно — в некоторых ситуациях определенные виды имен запрещены. Я могу долго рассуждать, сколько вреда несут искусственные, ничем не оправданные ограничения на имена, придуманные во времена телетайпов и даже тогда взятые, как правило, с потолка; сколько сил и энергии вообще на это тратят программисты, как часто подобные соглашения античеловечны (почему у кого-то нельзя пробел? Дефис? Юникод? И т.п.). Тут ничего не поделаешь, конечно. Моя задача — посеять правильную идею.

https://tonsky.livejournal.com/304954.html?

43
Q

Python descriptors

A

Теория для создания своего pydantic

https://realpython.com/python-descriptors/

44
Q

Python dataclass from scratch

A

Теория для создания своего pydantic

https://xavd.id/blog/post/python-dataclasses-from-scratch/

45
Q

Pydentic - загрузка переменных из .env

A

config = Config(_env_file=dotenv.find_dotenv())

46
Q

Почему нежелательно использовать pytest_addoption?

A
  1. Ограничивает, можно использовать полученные значения только внутри папки фикстур
  2. Нарушает консистентность (zen of python)
47
Q

Литералы в pydantic

A

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

BrowserType = Literal['chrome', 'firefox', 'edge']

class Config(pydantic_settings.BaseSettings):
    driver_name: BrowserType = 'chrome'
48
Q

TDD

A

Test driven development
1. Tests first
2. Written by developers

49
Q

BDD

https://youtu.be/GEcpbOaS6Bw

A

BDD - outside in (т.е. сначала требования и тесты). Без outside in - это не BDD, а bullshit driven development.

  • With tests
  • As documentation
  • as behaviours
  • focused on business
  • as specification
  • i.e. provided from BA side a tool to define unclear requirements
  • in ubiquitous language discussed between all stakeholders (BA, Test, Dev, etc.)
50
Q

Можно ли выносить локаторы в отдельный модуль?

A

Нет. locators.fisrt_name - это анти-паттерн.

В рамках PageObject все локаторы инкапсулируются внутри класса

51
Q

Функция map

A

Применение функции к каждому элементу списка

Функция map() в Python применяется для применения функции к каждому элементу итерируемого объекта (например, списка) и возвращает итератор. Основные аспекты:

Синтаксис

map(function, iterable, ...)

Пример использования

numbers = [1, 2, 3, 4]
squared = map(lambda x: x**2, numbers)
print(list(squared))  # Вывод: [1, 4, 9, 16]

Параметры
* function: Функция, которую нужно применить.
* iterable: Итерируемый объект, элементы которого будут обработаны.

Особенности
* Можно использовать несколько итерируемых объектов.
* Возвращает итератор, который нужно преобразовать в список или другой тип данных для отображения.

____________________
~~~
list(map(str, ‘Hello world!’))
[‘H’, ‘e’, ‘l’, ‘l’, ‘o’, ‘ ‘, ‘w’, ‘o’, ‘r’, ‘l’, ‘d’, ‘!’]
~~~

Возведение списка в квадрат:

ls = [1, 2, 3, 4, 5, 6, 7, 8, 9]
list(map(lambda x: x * x, ls))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
52
Q

Фукнция reduce

A

Применяет указанную функцию к элементам последовательности, сводя её к единственному значению.

  • func : Функция, которую требуется применить к элементам последовательности. Должна принимать два аргумента, где первый аргумент — аккумулированное ранее значение, а второй — следующий элемент последовательности.
  • iterable : Последовательность, элементы которой требуется свести к единственному значению. Если последовательность пуста и не задан initializer, то возбуждается TypeError.
  • initializer=None : Базовое значение, с которого требуется начать отсчёт. Оно же будет возвращено, если последовательность пуста.

reduce(function, iterable[, initializer])

from functools import reduce

numbers = [1, 2, 3, 4]

product = reduce(lambda x, y: x*y, numbers)

print(product) # Вывод: 24

В данном случае происходит полседовательное умножение всех элементов:
12 -> 23 -> 64 -> 245

Т.е. получается - скармливает в простую функцию (например x + y) значения итератора по очереди, где на месте x сначала первое значение, а потом результат прошлой операции

53
Q

Функция filter

A

Отфильтровывает то, что не проходит и возвращает все остальное.
Объект фильтра — это итерируемый объект. Он сохраняет те элементы, для которых функция вернула True

Синтаксис: filter(function, iterable)

numbers = [1, 2, 3, 4, 5, 7, 10, 11]

def filter_num(num):
    if(num % 2) != 0:
        return True
    else:
        return False
				
out_filter = filter(filter_num, numbers)
print("Отфильтрованный список: ", list(out_filter))
54
Q

Модуль logger в python

A

Для логирования шагов в консоль. Вместо print

55
Q

Нужно ли проверять значения в БД в интеграционных\E2E тестах?

A

в большистве случаев лезть в базу и что-то там сравнивать в тестах системного уровня - это плохая практика.
Потому что на системном уровне мы проверямем “поведение”, а БД - это “кишки, тех детали”. Если мы в тестах на поведение завязываемся на тех детали - то такие тесты становятся слишком завязаными на тех детали, и аз-за того хрупкими с точки зрения поведения (поведение не поменялось и не сломалось - а тесты упали). И мы получается не поведение уже начинаем тестировать а техническую реализацию, что как бы противоречит всей идее тестирования на системном уровне.

В БД принято лезть и что-то там проверять - на более низкоуровневых тестах.

Вот классический гайд на автоматизацию по все уровням https://martinfowler.com/articles/practical-test-pyramid.html

56
Q

Single page application frameworks

A
  • react,
  • angular,
  • ember.js
57
Q

Назначение абстрактного класса и интерфейса.

Как применить в случае птиц, самолётов, дирижаблей, воздушных шаров?

A

Для птиц у нас есть много объединяющих элементов: крылья, лапки, перья, клювы - для этого используется абстрактный класс

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

58
Q

DuckTyping

A

Duck typing — это принцип в программировании, согласно которому тип объекта определяется не по его классу, а по его поведению (методам и свойствам). Если объект ведет себя как определенный тип (например, имеет метод quack), его можно использовать как этот тип, независимо от фактического класса. Это позволяет писать более гибкий и удобный код, но требует внимательности, чтобы избежать ошибок при выполнении.

59
Q

Python. Отличие == от is.

A

== сравнивает значения.
is сравнивает идентичность объектов (их местоположение в памяти).

60
Q

Python. Сосчитать количество повторений слова, буквы или цифры в строке

A

str.count(‘word’)
str.count(‘1’)
str.count(‘a’)