Skip to content

Pytest usage

Mikhail Trifonov edited this page Jun 5, 2019 · 5 revisions

Project example

conftest.py

from logging.config import dictConfig

import pytest
from _pytest.config import Parser
from _pytest.fixtures import SubRequest
from pagium import Remote

pytest_plugins = ['pages']

DEFAULT_PROJECT_URL = 'http://project'

DEFAULT_SELENIUM_TCP = 'selenium:4444'
DEFAULT_BROWSER_NAME = 'chrome'

DEFAULT_POLLING_TIMEOUT = 30
DEFAULT_POLLING_DELAY = 0.5

DEFAULT_IMPLICITLY_WAIT = 15
DEFAULT_LOAD_PAGE_TIMEOUT = 30
DEFAULT_SCRIPT_TIMEOUT = 15

DEFAULT_WINDOW_SIZE = '1360x1020'


def pytest_addoption(parser: Parser):
    """
    Add options here
    """
    parser.addoption(
        '--project-url',
        action='store',
        default=DEFAULT_PROJECT_URL,
        help=f'Travel web url. "{DEFAULT_PROJECT_URL}" by default.',
    )
    parser.addoption(
        '--selenium-tcp',
        action='store',
        default=DEFAULT_SELENIUM_TCP,
        help=f'Selenium hub TCP. "{DEFAULT_SELENIUM_TCP}" by default.',
    )
    parser.addoption(
        '--browser-name',
        action='store',
        default=DEFAULT_BROWSER_NAME,
        help=f'Browser name. "{DEFAULT_BROWSER_NAME}" by default.',
    )
    parser.addoption(
        '--polling-timeout',
        type=int,
        action='store',
        default=DEFAULT_POLLING_TIMEOUT,
        help=f'Long polling timeout in seconds. "{DEFAULT_POLLING_TIMEOUT}" by default.',
    )
    parser.addoption(
        '--polling-delay',
        type=float,
        action='store',
        default=DEFAULT_POLLING_DELAY,
        help=f'Long polling delay in seconds. "{DEFAULT_POLLING_DELAY}" by default.',
    )
    parser.addoption(
        '--implicitly-wait',
        type=int,
        action='store',
        default=DEFAULT_IMPLICITLY_WAIT,
        help=f'Implicitly wait timeout in seconds. "{DEFAULT_IMPLICITLY_WAIT}" by default.',
    )
    parser.addoption(
        '--load-page-timeout',
        type=int,
        action='store',
        default=DEFAULT_LOAD_PAGE_TIMEOUT,
        help=f'Load page timeout in seconds. "{DEFAULT_LOAD_PAGE_TIMEOUT}" by default.',
    )
    parser.addoption(
        '--window-size',
        type=str,
        action='store',
        default=DEFAULT_WINDOW_SIZE,
        help=f'Browser window size. "{DEFAULT_WINDOW_SIZE}" by default.',
    )
    parser.addoption(
        '--script-timeout',
        type=int,
        action='store',
        default=DEFAULT_SCRIPT_TIMEOUT,
        help=f'Script timeout in seconds. "{DEFAULT_SCRIPT_TIMEOUT}" by default.',
    )


@pytest.fixture('session')
def is_debug(request: SubRequest):
    return request.config.getoption('--debug')


@pytest.fixture('session')
def project_url(request: SubRequest):
    return request.config.getoption('--project-url')


@pytest.fixture('session')
def selenium_tcp(request: SubRequest):
    return request.config.getoption('--selenium-tcp')


@pytest.fixture('session')
def browser_name(request: SubRequest):
    return request.config.getoption('--browser-name')


@pytest.fixture('session')
def polling_timeout(request: SubRequest):
    return request.config.getoption('--polling-timeout')


@pytest.fixture('session')
def polling_delay(request: SubRequest):
    return request.config.getoption('--polling-delay')


@pytest.fixture('session')
def implicitly_wait(request: SubRequest):
    return request.config.getoption('--implicitly-wait')


@pytest.fixture('session')
def load_page_timeout(request: SubRequest):
    return request.config.getoption('--load-page-timeout')


@pytest.fixture('session')
def window_size(request: SubRequest):
    return request.config.getoption('--window-size')


@pytest.fixture('session')
def script_timeout(request: SubRequest):
    return request.config.getoption('--script-timeout')


@pytest.fixture('function')
def browser(selenium_tcp: str,
            browser_name: str,
            polling_timeout: int,
            polling_delay: float,
            implicitly_wait: int,
            load_page_timeout: int,
            window_size: str,
            script_timeout: int):
    driver = Remote(
        command_executor=f'http://{selenium_tcp}/wd/hub',
        desired_capabilities={'browserName': browser_name},
        polling_timeout=polling_timeout,
        polling_delay=polling_delay,
    )

    with driver.disable_polling():
        driver.implicitly_wait(implicitly_wait)
        driver.set_script_timeout(script_timeout)
        driver.set_page_load_timeout(load_page_timeout)
        driver.set_window_size(*window_size.split('x'))

    try:
        yield driver
    finally:
        with driver.disable_polling():
            driver.quit()


@pytest.fixture('session', autouse=True)
def setup_session(is_debug: bool):
    log_level = 'DEBUG' if is_debug else 'INFO'

    dictConfig({
        'version': 1,
        'formatters': {
            'basic': {
                'format': '%(asctime)-15s %(levelname)s %(message)s',
            },
        },
        'handlers': {
            'console': {
                'class': 'logging.StreamHandler',
                'level': log_level,
                'formatter': 'basic',
            },
            'null': {
                'class': 'logging.NullHandler',
                'level': 'INFO',
            },
        },
        'loggers': {
            'pagium': {
                'handlers': ['console'],
                'level': log_level,
                'propagate': False,
            },
            'selenium': {
                'handlers': ['console'],
                'level': log_level,
                'propagate': False,
            },
            'root': {
                'handlers': ['null'],
            },
        },
    })

pages/my_page.py

from pagium import Page, PageElement, WebElement, By, controls

class MyPage(Page):

    __path__ = '/'

    class Header(WebElement):

        class AuthForm(WebElement):

            username = PageElement(controls.Input, by=By.NAME, value='username')
            password = PageElement(controls.Input, by=By.NAME, value='password')
            submit = PageElement(by=By.TAG_NAME, value='button', hook=lambda we: we.click)

        auth_form = PageElement(AuthForm, by=By.TAG_NAME, value='form')

    header = PageElement(Header, by=By.CSS_SELECTOR, value='[class*="header-wrapper"]') 

pages/init.py

import pytest
from pagium import Remote

from pages.my_page import MyPage


@pytest.fixture('function')
def my_page(browser: Remote, project_url: str):
    return MyPage(browser, project_url)

tests/test_example.py

from pages import MyPage


def test_example(my_page: MyPage):
    with my_page as page:
        my_page.header.auth_form.username.fill('username')
        my_page.header.auth_form.password.fill('password')
        my_page.header.auth_form.submit()

Results

In this project we have

Console debug requests with --debug flag

pytest --debug

Driver settings from command line

pytest --implicitly-wait 15 --window-size 1360x1020 ...

Run tests with docker

Local run with VNC debug

docker run -d --name selenium --net host selenium/standalone-chrome-debug:3.141.59-antimony
pytest --selenium-tcp localhost:4444 ...

Gitlab CI file example

ui tests:
  image: python:3.7
  services:
    - selenium/standalone-chrome:3.141.59-antimony
    - gitlab-host.ru/project-space/project-name:latest
  before_script:
    - pip install pytest pagium
  script:
    - pytest --project-url http://localhost:8080 --selenium-tcp localhost:4444

Clone this wiki locally