Skip to content

Commit

Permalink
implement multiple browser instances possibility for a single test
Browse files Browse the repository at this point in the history
  • Loading branch information
bubenkoff committed Sep 6, 2014
1 parent 1f28040 commit f136da1
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 123 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -46,3 +46,4 @@ nosetests.xml
/lib
/include
/src
/.env
6 changes: 3 additions & 3 deletions CHANGES.rst
@@ -1,10 +1,10 @@
Changelog
=========

1.0.5
-----
Unreleased
----------

- added remote webdriver url command line argument (bubenkoff)
- added possibility to have multiple browser instances for single test (amakhnach, bubenkoff)


1.0.4
Expand Down
18 changes: 18 additions & 0 deletions README.rst
Expand Up @@ -31,6 +31,24 @@ Fixtures
Get the splinter's Browser. Fixture is underneath session scoped, so browser process is started
once per test session.

* browser_instance_getter
Function to create an instance of the browser. This fixture is required only if you need to have
multiple instances of the Browser in a single test at the same time. Example of usage:

.. code-block:: python
@pytest.fixture
def admin_browser(browser_instance_getter):
"""Admin browser fixture."""
# browser_instance_getter function receives single argument - parent fixture
# in our case it's admin_browser
return browser_instance_getter(admin_browser)
def test_2_browsers(browser, admin_browser):
"""Test using 2 browsers at the same time."""
browser.visit('http://google.com')
admin_browser.visit('http://admin.example.com')
* splinter_selenium_implicit_wait
Implicit wait timeout to be passed to Selenium webdriver.
Fixture gets the value from the command-line option splinter-implicit-wait (see below)
Expand Down
125 changes: 64 additions & 61 deletions pytest_splinter/plugin.py
Expand Up @@ -159,9 +159,41 @@ def splinter_window_size():
return (1366, 768)


@pytest.fixture(scope="session") # pragma: no cover
@pytest.fixture(scope='session')
def splinter_session_scoped_browser(request):
"""Flag to keep single browser per test session."""
return request.config.option.splinter_session_scoped_browser


@pytest.fixture(scope='session')
def browser_pool(request, splinter_close_browser):
"""Browser 'pool' to emulate session scope but with possibility to recreate browser."""
pool = {}

def fin():
for browser in pool.values():
try:
browser.quit()
except (IOError, OSError):
pass

if splinter_close_browser:
request.addfinalizer(fin)

return pool


@pytest.fixture(scope='session')
def browser_patches(splinter_selenium_socket_timeout):
"""Browser monkey patches."""
patch_webdriver(splinter_selenium_socket_timeout)
patch_webdriverelement()


@pytest.fixture
def browser_instance_getter(
request,
browser_patches,
splinter_selenium_socket_timeout,
splinter_selenium_implicit_wait,
splinter_selenium_speed,
Expand All @@ -174,13 +206,11 @@ def browser_instance_getter(
splinter_firefox_profile_preferences,
splinter_driver_kwargs,
splinter_window_size,
browser_pool,
):
"""Splinter browser instance getter. To be used for getting of plugin.Browser's instances.
:return: get_instance function. Each time this function will return new instance of plugin.Browser class.
:return: function(parent). Each time this function will return new instance of plugin.Browser class.
"""
patch_webdriver(splinter_selenium_socket_timeout)
patch_webdriverelement()

kwargs = {}

if splinter_webdriver == 'firefox':
Expand All @@ -196,7 +226,7 @@ def browser_instance_getter(
if splinter_driver_kwargs:
kwargs.update(splinter_driver_kwargs)

def get_instance():
def get_browser():
browser = Browser(
splinter_webdriver, visit_condition=splinter_browser_load_condition,
visit_condition_timeout=splinter_browser_load_timeout, **copy.deepcopy(kwargs)
Expand All @@ -210,65 +240,38 @@ def get_instance():
return browser
# set automatic download directory for firefox

return get_instance


@pytest.fixture(scope='session')
def browser_pool(request, splinter_close_browser):
"""Browser 'pool' to emulate session scope but with possibility to recreate browser."""
pool = {}

def fin():
for _, browser in pool.items():
def prepare_browser(parent):
browser_key = id(parent)
browser = browser_pool.get(browser_key)
if not splinter_session_scoped_browser:
browser = get_browser()
if splinter_close_browser:
request.addfinalizer(browser.quit)
elif not browser:
browser = browser_pool[browser_key] = get_browser()
else:
try:
browser.quit()
except (IOError, OSError):
pass

if splinter_close_browser:
request.addfinalizer(fin)

return pool


@pytest.fixture(scope='session')
def splinter_session_scoped_browser(request):
"""Flag to keep single browser per test session."""
return request.config.option.splinter_session_scoped_browser

browser.driver.delete_all_cookies()
except IOError:
# we lost browser, try to restore the justice
try:
browser.quit()
except Exception:
pass
browser = browser_pool[browser_key] = get_browser()

browser.visit_condition = splinter_browser_load_condition
browser.visit_condition_timeout = splinter_browser_load_timeout
browser.driver.get('about:blank')
return browser

@pytest.fixture # pragma: no cover
def browser(
request, browser_pool, splinter_webdriver, splinter_session_scoped_browser,
splinter_close_browser, splinter_browser_load_condition, splinter_browser_load_timeout,
browser_instance_getter):
"""Splinter browser wrapper instance. To be used for browser interaction.
return prepare_browser

Function scoped (cookies are clean for each test and on blank).
"""
if not splinter_session_scoped_browser:
browser_pool = {}
browser = browser_instance_getter()
if splinter_close_browser:
request.addfinalizer(browser.quit)
elif not browser_pool:
browser = browser_pool["browser"] = browser_instance_getter()
else:
browser = browser_pool["browser"]
try:
browser.driver.delete_all_cookies()
except IOError:
# we lost browser, try to restore the justice
try:
browser.quit()
except Exception:
pass
browser = browser_pool["browser"] = browser_instance_getter()

browser.visit_condition = splinter_browser_load_condition
browser.visit_condition_timeout = splinter_browser_load_timeout
browser.driver.get('about:blank')
return browser
@pytest.fixture
def browser(browser_instance_getter):
"""Browser fixture."""
return browser_instance_getter(browser)


def pytest_addoption(parser): # pragma: no cover
Expand Down
Empty file.
24 changes: 24 additions & 0 deletions tests/mocked_browser/conftest.py
@@ -0,0 +1,24 @@
"""Configuration for pytest runner."""

pytest_plugins = 'pytester'

import mock
import pytest


@pytest.fixture(scope='session')
def splinter_session_scoped_browser():
"""Make it test scoped."""
return False


@pytest.fixture(autouse=True)
def mocked_browser(request):
"""Mock splinter browser."""
mocked_browser = mock.MagicMock()
mocked_browser.driver = mock.MagicMock()
mocked_browser.driver.profile = mock.MagicMock()
patcher = mock.patch('pytest_splinter.plugin.splinter.Browser', lambda *args, **kwargs: mocked_browser)
request.addfinalizer(patcher.stop)
patcher.start()
return mocked_browser
Expand Up @@ -5,29 +5,6 @@

import pytest

import mock
import splinter


def setup_module():
"""Mock splinter browser."""
mocked_browser = mock.MagicMock()
mocked_browser.driver = mock.MagicMock()
mocked_browser.driver.profile = mock.MagicMock()
splinter._Browser = splinter.Browser
splinter.Browser = lambda *args, **kwargs: mocked_browser


def teardown_module():
"""Unmock browser back."""
splinter.Browser = splinter._Browser


@pytest.fixture
def browser_pool():
"""Browser fixture. Overriden to make it test-scoped and mock."""
return {}


def test_wait_for_condition(
browser,
Expand Down
Expand Up @@ -2,28 +2,9 @@

import pytest

import mock

from pytest_splinter import plugin


@pytest.fixture(autouse=True, scope='module')
def mocked_browser(request):
"""Mock splinter browser."""
mocked_browser = mock.MagicMock()
mocked_browser.driver = mock.MagicMock()
mocked_browser.driver.profile = mock.MagicMock()
mock.patch('splinter.Browser', lambda *args, **kwargs: mocked_browser)
request.addfinalizer(mock.patch.stopall)
return mocked_browser


@pytest.fixture
def browser_pool():
"""Browser fixture. Overriden to make it test-scoped and mock."""
return {}


def test_wait_for_condition(
browser,
splinter_browser_load_condition,
Expand Down
14 changes: 0 additions & 14 deletions tests/test_plugin.py
Expand Up @@ -85,17 +85,3 @@ def test_get_current_window_info(browser):
def test_current_window_is_main(browser):
"""Test browser's driver current_window_is_main."""
assert browser.driver.current_window_is_main()


def test_browser_instance_getter(browser_instance_getter):
"""Test that browser_instance_getter fixture return function and if run this function then each time we will get
different instance of plugin.Browser class."""
assert callable(browser_instance_getter)

browser1 = browser_instance_getter()
browser2 = browser_instance_getter()

assert isinstance(browser1, plugin.Browser)
assert isinstance(browser2, plugin.Browser)

assert id(browser1) != id(browser2)
5 changes: 2 additions & 3 deletions tox.ini
Expand Up @@ -5,12 +5,11 @@ indexserver=
pypi = https://pypi.python.org/simple

[testenv]
commands= py.test --pep8 --junitxml={envlogdir}/junit-{envname}.xml
commands= py.test pytest_splinter tests --pep8 --junitxml={envlogdir}/junit-{envname}.xml
deps = -r{toxinidir}/requirements-testing.txt

[testenv:py27-coverage]
commands= py.test --cov=pytest_splinter --pep8 --junitxml={envlogdir}/junit-{envname}.xml
commands= py.test tests --cov=pytest_splinter --junitxml={envlogdir}/junit-{envname}.xml

[pytest]
addopts=tests
pep8maxlinelength=120

0 comments on commit f136da1

Please sign in to comment.