Традиционный метод предварительной и последующей обработки pytest был представлен ранее. Из некоторых примеров мы знаем, что он имеет определенные ограничения при обработке сценариев предварительной и последующей обработки. Именно поэтому была введена функция декоратора фикстур. Fixture — это основная функция pytest, а также функция выделения. Только тогда вы сможете гибко обрабатывать множество специальных сценариев. используйте pytest с легкостью!
Целью приспособления является обеспечение фиксированной базовой линии, относительно которой можно надежно и многократно проводить испытания. Фикстуры обеспечивают значительные улучшения по сравнению с традиционным модульным тестированием (настройка/демонтаж):
1. Имейте независимые имена и активируйте их, объявив их использование из тестовых функций, модулей, классов или всего проекта.
2. Модульная реализация: каждый прибор может вызывать друг друга.
3. Область применения фикстур простирается от простых модулей до сложных функциональных тестов, что позволяет параметризовать фикстуры и тестовые сценарии на основе параметров конфигурации и компонентов или в рамках функции, класса, модуля или всей области сеанса тестирования.
Давайте сначала посмотрим на определение функции fixment:
def fixture(
callable_or_scope=None,
*args,
scope="function",
params=None,
autouse=False,
ids=None,
name=None
):
"""Decorator to mark a fixture factory function.
This decorator can be used, with or without parameters, to define a
fixture function.
The name of the fixture function can later be referenced to cause its
invocation ahead of running tests: test
modules or classes can use the ``pytest.mark.usefixtures(fixturename)``
marker.
Test functions can directly use fixture names as input
arguments in which case the fixture instance returned from the fixture
function will be injected.
Fixtures can provide their values to test functions using ``return`` or ``yield``
statements. When using ``yield`` the code block after the ``yield`` statement is executed
as teardown code regardless of the test outcome, and must yield exactly once.
:arg scope: the scope for which this fixture is shared, one of
``"function"`` (default), ``"class"``, ``"module"``,
``"package"`` or ``"session"`` (``"package"`` is considered **experimental**
at this time).
This parameter may also be a callable which receives ``(fixture_name, config)``
as parameters, and must return a ``str`` with one of the values mentioned above.
See :ref:`dynamic scope` in the docs for more information.
:arg params: an optional list of parameters which will cause multiple
invocations of the fixture function and all of the tests
using it.
The current parameter is available in ``request.param``.
:arg autouse: if True, the fixture func is activated for all tests that
can see it. If False (the default) then an explicit
reference is needed to activate the fixture.
:arg ids: list of string ids each corresponding to the params
so that they are part of the test id. If no ids are provided
they will be generated automatically from the params.
:arg name: the name of the fixture. This defaults to the name of the
decorated function. If a fixture is used in the same module in
which it is defined, the function name of the fixture will be
shadowed by the function arg that requests the fixture; one way
to resolve this is to name the decorated function
``fixture_<fixturename>`` and then use
``@pytest.fixture(name='<fixturename>')``.
"""
Примерно перевел:
def fixture(
callable_or_scope=None,
*args,
scope="function",
params=None,
autouse=False,
ids=None,
name=None
):
"""
1. Fixture можно использовать для обозначения функций прибора независимо от того, имеет он параметры или нет;
2. Тестовые модули или классы можно пометить с помощью декоратора «pytest.mark.usefixture(fixturename)». После маркировки имя_фикстуры будет вызываться перед запуском каждого тестового примера;
3. Тестовая функция может напрямую использовать имя прибора в качестве входного параметра. В этом случае экземпляр прибора будет введен из возвращаемой функции.
4. Крепеж можно использовать. return 'или' yield ', чтобы предоставить их значения для проверки операторов функций. Когда выполняется блок кода, следующий за оператором «yield», он должен быть сгенерирован ровно один раз, независимо от результата теста.
:arg scope: Существует 4 уровня области видимости: по умолчанию используется функция, за ней следует класс, затем модуль и сеанс.
:arg params: Необязательный список параметров, который приведет к использованию нескольких аргументов при вызовах функции фиксации и всех тестов.
:arg autouse: если true, активировать приспособление для всех тестов. func может это увидеть. Если значение False (по умолчанию), для активации прибора явно требуется ссылка.
:arg ids: Список идентификаторов строк, соответствующих каждому параметру, поэтому они являются частью идентификатора теста. Если идентификатор не указан, они будут автоматически заменены параметрами.
:arg имя: Имя устройства. Имя декорируемой функции по умолчанию. Если существует один и тот же модуль, используется Какой из них определен для приспособления?,Имя функции прибора будет скрыто параметрами функции, необходимыми для прибора. Это один из способов решения этой проблемы;,Модифицированные функции могут быть названы'fixture_<fixturename>'а затем использовать
@pytest.fixture (name = ' < fixturename > ')。
"""
Параметр области в основном управляет областью действия прибора, а логический приоритет: сеанс. > module > class > function. Существует четыре варианта параметра области: функция (уровень тестовой функции), класс (уровень тестового класса), модуль (уровень тестового модуля «.py»), сеанс (несколько уровней файлов). По умолчанию используется функциональный уровень. Здесь следует отметить, что модули, упомянутые в документе pytest, называются файлами «.py». То есть модуль означает py-файл.
Введение в уровень: Уровень функции (для функций): запуск перед запуском каждого тестового примера. Уровень класса (для тестовых классов): каждый класс выполняется один раз (запускается перед выполнением всех тестовых случаев, этот узел начинается с тестового примера, который представляет приспособление), класс может иметь несколько методов тестирования (сценариев использования). Уровень модуля (для одного модуля): выполняется один раз для каждого модуля (.py), независимо от метода тестирования в классе или метода тестирования вне класса. Уровень сеанса (для нескольких модулей): он вызывается один раз несколькими файлами и может вызываться в файлах .py. Каждый файл .py является модулем.
Если декоратор @pytest.fixture() не записывает параметры, по умолчанию используется значениеscope="function". Его область видимости запускается один раз перед каждым тестовым примером, а код уничтожения запускается после запуска тестового примера.
В предыдущей статье это уже было представлено, и я вставлю сюда код:
(Одна функция прибора, без класса)
# Создайте функцию фиксации (без класса) — метод 1, передайте ее как параметр в качестве области видимости: функции
@pytest.fixture()
def login():
print("Введите номер счета")
a = "account"
return a
def test_001(login):
print("Учетная запись: %s"%login)
assert login == "account"
def test_002():
print("Нажмите, чтобы войти")
if __name__ == '__main__':
pytest.main()
Результаты запуска:
plugins: allure-pytest-2.8.6, rerunfailures-5.0
collected 2 items
fixtrue_001.py Введите номер счета
Номер счета: account
.Нажмите, чтобы войти
.
================================================================================== 2 passed in 0.02s ===================================================================================
(Одна функция прибора с классом)
# Создайте функцию фиксации (в классе) — метод 2, передайте ее как параметр в качестве области видимости: функции
@pytest.fixture(scope="function")
def login():
print("Введите номер счета")
a = "account"
return a
class TestLogin:
def test_001(self,login):
print("Введите номер счета: %s"%login)
assert login == "account"
def test_002(self):
print("")
if __name__ == '__main__':
pytest.main(["-s","fixtrue_001.py"])
Результаты запуска:
plugins: allure-pytest-2.8.6, rerunfailures-5.0
collected 2 items
fixtrue_001.py Введите номер счета
Введенный номер счета: account
.Используйте вариант 2
.
================================================================================== 2 passed in 0.02s ===================================================================================
Некоторые сценарии, такие как выход из системы после входа в систему, требуют обработки двух функций фиксации. Примеры:
# функция фиксации (в классе) Передано как несколько параметров
@pytest.fixture()
def login():
print("Введите номер счета")
a = "account"
return a
@pytest.fixture()
def logout():
печать("Выход")
class TestLogin:
def test_001(self,login):
print("Введите номер счета: %s"%login)
assert login == "account"
def test_002(self,logout):
печать("Выход")
def test_003(self,login,logout):
print("Шаг 1:%s"%login)
print("Шаг 2:%s"%выход)
if __name__ == '__main__':
pytest.main()
Результаты запуска:
plugins: allure-pytest-2.8.6, rerunfailures-5.0
collected 3 items
fixtrue_001.py Введите номер счета
Введенный номер счета: account
.покидать
покидать
.Введите номер счета
покидать
Шаг 1: аккаунт
Шаг 2: Нет
.
================================================================================== 3 passed in 0.03s ===================================================================================
Прошивка прибора может вызываться методами тестирования или самой прошивкой.
Например:
# fixtrue используется в качестве параметра и передается при вызове друг друга.
@pytest.fixture()
def account():
a = "account"
print("Введите номер счета:%s"%a)
@pytest.fixture()
def login(account):
print("Нажмите, чтобы войти")
class TestLogin:
def test_1(self,login):
print("Результат операции: %s"%login)
def test_2(self,account):
print("Аккаунт: %s"%account)
def test_3(self):
print("Тестовый пример 3")
if __name__ == '__main__':
pytest.main()
Результаты запуска:
plugins: allure-pytest-2.8.6, rerunfailures-5.0
collected 3 items
fixtrue_001.py Введите номер счета:account
Нажмите, чтобы войти
Результат операции: Нет
.Введите номер счета:account
счет: None
.Тестовый пример 3
.
================================================================================== 3 passed in 0.02s ===================================================================================
Когда приспособление находится на уровне класса, оно делится на две ситуации:
Во-первых, все тестовые методы (кейсы) в тестовом классе используют имя функции фикстуры. В этом случае фикстура выполняется только один раз, прежде чем будут выполнены все тестовые случаи в классе.
Пример демонстрации:
# объем приспособления scope = 'class'
@pytest.fixture(scope='class')
def login():
a = '123'
print("Введите номер счетапароль Авторизоваться")
class TestLogin:
def test_1(self,login):
print("Сценарий использования 1")
def test_2(self,login):
print("Сценарий использования 2")
def test_3(self,login):
print("Сценарий использования 3")
if __name__ == '__main__':
pytest.main()
Результаты запуска:
collected 3 items
fixtrue_001.py Введите номер счетапароль Авторизоваться
Вариант использования 1
.Используйте вариант 2
.Используйте вариант 3
.
================================================================================== 3 passed in 0.02s ===================================================================================
Во-вторых, только некоторые методы тестирования в тестовом классе используют имя функции фиксации. В этом случае фикстура отсчитывается только с позиции первого тестового примера, который использует функцию фикстуры в классе, а все последующие тестовые примеры выполняются только. один раз. Тестовые примеры перед этой позицией игнорируются.
# объем приспособления scope = 'class'
@pytest.fixture(scope='class')
def login():
a = '123'
print("Введите номер счетапароль Авторизоваться")
class TestLogin:
def test_1(self):
print("Сценарий использования 1")
def test_2(self,login):
print("Сценарий использования 2")
def test_3(self,login):
print("Сценарий использования 3")
def test_4(self):
print("Сценарий использования 4")
if __name__ == '__main__':
pytest.main()
Результаты запуска:
collected 4 items
fixtrue_001.py Вариант использования 1
.Введите номер счетапароль Авторизоваться
Вариант использования 2
.Используйте вариант 3
.Используйте вариант 4
.
================================================================================== 4 passed in 0.03s ===================================================================================
Если приспособление является модулем, все тестовые примеры в текущем файле модуля (.py) будут выполнены один раз перед запуском. Пример следующий:
# fixture scope = 'module'
@pytest.fixture(scope='module')
def login():
печать("Войти")
def test_01(login):
print("Сценарий использования 01")
def test_02(login):
print("Сценарий использования 02")
class TestLogin():
def test_1(self,login):
print("Сценарий использования 1")
def test_2(self,login):
print("Сценарий использования 2")
def test_3(self,login):
print("Сценарий использования 3")
if __name__ == '__main__':
pytest.main()
Результаты запуска:
collected 5 items
fixtrue_001.py Авторизоваться
Вариант использования 01
.Используйте вариант 02
.Вариант использования 1
.Используйте вариант 2
.Используйте вариант 3
.
================================================================================== 5 passed in 0.03s ===================================================================================
Метод настройки аналогичен методу настройки уровня модуля. Следует отметить, что уровень сеанса обычно используется несколькими файлами .py, поэтому объявление функции префикса обычно находится в conftest.py.
Ее функция выполняется только один раз в нескольких тестовых модулях (файлах .py) и выполняется перед первым выполненным тестовым примером в тестовом примере, передающем имя функции.
Если они находятся в одном модуле (в файле .py), свойства сеанса и модуля совпадают. Пример следующий:
import pytest
@pytest.fixture(scope="session")
def login():
print("\nВведите имя пользователяпароль Авторизоваться! configtest")
yield
print("покидать Авторизоваться")
def test_cart(login):
print('Вариант использования 1,Авторизоваться, а затем выполнить функцию добавления в корзину')
def test_search():
print('Вариант использования 2,Не авторизовать работу функции запроса')
def test_pay(login):
print('Вариант использования 3,Авторизоваться После выполнения операции платежной функции')
if __name__ == '__main__':
pytest.main()
Результаты запуска:
plugins: allure-pytest-2.8.6, rerunfailures-5.0
collected 3 items
fixtrue_003.py
Введите имя пользователяпароль Авторизоваться! configtest
Вариант использования 1,Авторизоваться Затем выполните операцию добавления функции корзины покупок.
.Используйте вариант 2,Нет Авторизоваться Работа функции запроса
.Используйте вариант 3,Авторизоваться После выполнения операции платежной функции
.покидать Авторизоваться
================================================================================== 3 passed in 0.02s ===================================================================================
Если сеанс используется для нескольких файлов .py в разных модулях (в файлах .py) и записывается в файл conftest.py, имя файла conftest.py фиксировано, и pytest автоматически распознает файл.
Если вы поместите его в корневой каталог проекта, вы сможете вызывать его глобально. Если вы поместите его в определенный пакет, он будет действителен только в этом пакете. Пример следующий:
Создайте новый файл conftest.py в папке fixment_exp:
# fixture Фиксированный декоратор, область действия: область действия = 'session'
import pytest
@pytest.fixture(scope='session')
def login():
print("Введите номер счетапароль")
yield
print("Очистка данных завершена")
Создайте два новых тестовых файла:
# fixture scope = 'session',fixtrue_001.py
class TestLogin1():
def test_1(self,login):
print("Сценарий использования 1")
def test_2(self):
print("Сценарий использования 2")
def test_3(self,login):
print("Сценарий использования 3")
if __name__ == '__main__':
pytest.main()
# fixture scope = 'session',fixtrue_002.py
import pytest
class TestLogin2():
def test_4(self):
print("Сценарий использования 4")
def test_5(self):
print("Сценарий использования 5")
def test_6(self):
print("Сценарий использования 6")
if __name__ == '__main__':
pytest.main()
Чтобы запустить два тестовых файла одновременно, вы можете ввести в консоли:
pytest -s fixtrue_001.py fixtrue_002.py
Результаты запуска:
plugins: allure-pytest-2.8.6, rerunfailures-5.0
collected 6 items
fixtrue_001.py Введите номер счетапароль
Вариант использования 1
.Используйте вариант 2
.Используйте вариант 3
.
fixtrue_002.py Вариант использования 4
.Используйте вариант 5
.Используйте вариант 6
.Очистка данных завершена
================================================================================== 6 passed in 0.04s ===================================================================================
В приведенном выше примере, если тестовый пример test_1 не передает имя входа в систему, устройство фиксации будет выполнено один раз, прежде чем будет выполнен тестовый пример test_3. После того, как я его удалил, результаты повторного запуска будут следующими:
Областью фиксации, упомянутой выше, является сеанс, который обычно используется вместе с conftest.py, то есть передается как файл conftest.py.
История использования: если у нас много предварительных функций, не будет ли затруднительно писать их в каждом py-файле? Или, другими словами, что нам делать, если мы хотим использовать одну и ту же предварительную функцию во многих файлах py? Это то, что делает conftest.py.
Правила использования conftest.py:
Имя файла conftest.py фиксировано и не может быть изменено. conftest.py находится в том же пакете, что и выполняемый тестовый пример, и в пакете есть файл __init__.py. При его использовании импортировать conftest.py не нужно, он будет найден автоматически. Давайте посмотрим на небольшой каштан: мы создали новый файл conftest.py и записали в него объявление предварительной функции, мы создали новый тестовый модуль в том же пакете и передали предварительную функцию, объявленную в conftest; .py в тестовом методе. Установите имя функции.
# fixture Фиксированный декоратор, область действия: область действия = 'session'
import pytest
@pytest.fixture()
def login():
print("Введите номер счетапароль")
yield
print("Очистка данных завершена")
import pytest
# fixture scope = 'session',fixtrue_001.py
class TestLogin1():
def test_1(self,login):
print("Сценарий использования 1")
def test_2(self):
print("Сценарий использования 2")
def test_3(self,login):
print("Сценарий использования 3")
if __name__ == '__main__':
pytest.main()
Результаты запуска:
fixtrue_001.py Введите номер счетапароль
Вариант использования 1
.Очистка данных завершена
Вариант использования 2
.Введите номер счетапароль
Вариант использования 3
.Очистка данных завершена
================================================================================== 3 passed in 0.02s ===================================================================================
Каштан выше можно записать и по-другому, но вам нужно использовать другой декоратор.
После объявления предварительной функции в conftest.py, помимо передачи имени функции, мы также можем использовать декоратор @pytest.mark.userfixtures() в тестовом модуле.
Вкратце: процесс объявления предварительной функции аналогичен описанному выше; мы добавляем декоратор @pytest.mark.userfixtures() к каждому тестовому методу и передаем имя предварительной функции в качестве параметра; результаты такие же, как указано выше. Изображения больше не будут отображаться.
import pytest
# fixture scope = 'session',fixtrue_001.py
class TestLogin1():
@pytest.mark.usefixtures('login')
def test_1(self):
print("Сценарий использования 1")
@pytest.mark.usefixtures('login')
def test_2(self):
print("Сценарий использования 2")
def test_3(self):
print("Сценарий использования 3")
if __name__ == '__main__':
pytest.main()
fixtrue_001.py Введите номер счетапароль
Вариант использования 1
.Очистка данных завершена
Введите номер счетапароль
Вариант использования 2
.Очистка данных завершена
Вариант использования 3
.
================================================================================== 3 passed in 0.02s ===================================================================================
Если будет 100 тестовых методов, придется писать 100 декораторов. Разве это не неудобно?
В настоящее время, если вы хотите, чтобы каждый тестовый пример в модуле вызывал прошивку, вы также можете использовать метку pytestmark: следующий код (обратите внимание, что имя переменной pytestmark нельзя изменить), пример следующий:
import pytest
# fixture scope = 'session',fixtrue_001.py
pytestmark = pytest.mark.usefixtures('login')
class TestLogin1():
def test_1(self):
print("Сценарий использования 1")
def test_2(self):
print("Сценарий использования 2")
def test_3(self):
print("Сценарий использования 3")
if __name__ == '__main__':
pytest.main()
Результаты запуска:
fixtrue_001.py Введите номер счетапароль
Вариант использования 1
.Очистка данных завершена
Введите номер счетапароль
Вариант использования 2
.Очистка данных завершена
Введите номер счетапароль
Вариант использования 3
.Очистка данных завершена
================================================================================== 3 passed in 0.02s ===================================================================================
Примечание. Вы можете использовать @pytest.mark.usefixtures("fixture1", "fixture2"), чтобы пометить тестовую функцию или тестовый класс перед тестовой функцией. Это почти похоже на добавление параметров прибора в метод тестирования, но с помощью usefixtures вы не можете использовать возвращаемое значение прибора.
Дополнительное объяснение: область действия файла conftest.py находится в пределах текущего пакета (включая подпакеты), если функция вызывается, прошивка сначала будет искать в текущем тестовом классе, затем в модуле (файл .py) и затем текущий пакет (conftest.py), если до корневого каталога нет родительского пакета; если мы хотим объявить глобальный файл conftest.py, мы можем поместить его в корневой каталог;
Область conftest.py: тестовый класс > .py-файл > package
Четыре метода вызова фикстуры
1. Непосредственно передайте имя функционального параметра прибора методу в функции или классе.
2. Используйте декоратор @pytest.mark.usefixtures() для оформления.
3. usepytestmark = pytest.mark.usefixtures('login')
4.autouse=Истина используется автоматически
Первые три были обсуждены, а теперь обсудим четвертый.
Когда мы проводим автоматическое тестирование, существует множество вариантов использования. Очень неудобно, если каждый вариант использования должен передавать имя предварительной функции или декоратор.
На данный момент мы можем использовать параметр autouse (автоматическое использование) в @pytest.fixture() и установить для него значение true (по умолчанию — false), чтобы каждая тестовая функция автоматически вызывала предварительную функцию.
Чтобы дать небольшой каштан:
import pytest
@pytest.fixture(autouse="true")
def login():
print("Введите номер счетапароль")
class TestLogin:
def test_1(self):
print("Сценарий использования 1")
def test_2(self):
print("Сценарий использования 2")
if __name__ == '__main__':
pytest.main()
Результаты запуска:
============================== 2 passed in 0.05s ==============================
Process finished with exit code 0
Введите номер счетапароль
PASSED [ 50%]Вариант использования 1
Введите номер счетапароль
PASSED [100%]Вариант использования 2
Уведомление:
Для кода, который не зависит от какого-либо состояния системы или внешних данных и который необходимо запускать несколько раз, вы можете добавить в приспособление параметр autouse=True, например @pytest.fixture(autouse=True,scope="session") .
Однако, если возможно, попробуйте выбрать передачу параметров или использовать фикстуры вместо автоматического использования. autouse сделает логику тестовой функции менее понятной и более похожей на особый случай.
Ранее, знакомясь с определением фикстуры, мы говорили о параметрах: :arg params: необязательный список формальных параметров, который приведет к использованию нескольких параметров в вызовах функции фикстуры и во всех тестах.
1. Устройство может принимать параметры, а параметры поддерживают списки;
2. Значение по умолчанию: Нет;
3. Для каждого значения в param фикстура будет вызываться и выполняться один раз, точно так же, как выполнение цикла for для обхода значений в params.
Например:
import pytest
seq = [1,2,3]
@pytest.fixture(params=seq)
def test_data(request):
печать("параметр")
return request.param
class TestData:
def test_1(self,test_data):
print("вариант использования",test_data)
if __name__ == '__main__':
pytest.main()
Результаты запуска:
принцип:
существовать pytest Есть встроенный fixture называется запрос, представляет fixture статус звонящего. запрос Есть поле param,Вы можете использовать что-то вроде@pytest.fixture(param=tasks_list)
способ,существовать fixture используется в request.param
способкаквозвращаемое значение длятестовая функциявызов。в tasks_list Сколько элементов он содержит? fixture Она будет вызываться несколько раз и применяться к каждой тестовой функции, используемой в существовании.
идентификаторы обычно можно использовать с параметрами, поскольку они не указаны id, поэтому, когда существование выводится pytest воля fixture Имя плюс номер для идентификации, приспособление Вы также можете указать id,Например @pytest.fixture(param=tasks_list,ids=task_ids) идентификаторы могут быть списком или функцией pytest генерировать task логотип.
Числа, строки, логические значения и None будут проверять идентификатор. вэто обычное делонить Представительство,для других объектов,pytest создаст строку на основе имени параметра,Это можно сделать с помощьюids
Аргументы ключевых слов для настройки для тестированияIDизнить。
Например:
import pytest
seq = [1,2,3]
@pytest.fixture(params=seq,ids=["a","b","c"])
def test_data(request):
печать("параметр")
# print(request)
return request.param
class TestData:
def test_1(self,test_data):
print("вариант использования",test_data)
if __name__ == '__main__':
pytest.main()
Результаты запуска:
Обычно используют fixture Тестовая функция будет fixture Имя функции передается как параметр, но pytest Также разрешено fixture Переименовать。Просто используйте@pytest.fixture(name="new")
Вот и все,существоватьтестовая функцияиспользуется вдолжен fixture Вам нужно только пройти new Вот и все.
import pytest
@pytest.fixture(name="new_fixture")
def test_name():
pass
def test_1(new_fixture):
print("Тестовый вариант использования 1")
Результаты запуска:
collecting ... collected 1 item
fixture_test03.py::test_1 PASSED [100%]Тестовый вариант использования 1
============================== 1 passed in 0.03s ==============================
Подведем итог: по умолчанию имя функции прибора используется в качестве параметра.,То есть имя_теста передается как параметр.,Если использовать имя,Вам необходимо передать значение name в качестве нового имени параметра в тестовую функцию.
Вышеописанное представляет собой введение и использование функции фиксации в среде pytest. Каждый параметр вводится один раз. Принципы и методы хорошо понятны, поэтому существование можно легко использовать на практике. Если вам помогло или понравилось Автоматизированное тестированиеразвиватьиздруг,Вы можете присоединиться к коммуникационной группе QQ в правом нижнем углу, чтобы учиться и исследовать.,Еще больше полезной информации для вас
Наслаждаться.