Skip to content

Commit

Permalink
Merge pull request #195 from ElSnoMan/fix-make-dir
Browse files Browse the repository at this point in the history
update test_run fixture in conftest.py to be thread safe
  • Loading branch information
ElSnoMan authored May 21, 2021
2 parents 7b04f88 + ebad057 commit 3f764d5
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 120 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ jobs:
- uses: actions/checkout@v2

-
name: Set up Python 3.8
name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.8
python-version: 3.9

-
name: Install dependencies
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,5 @@ dmypy.json
Pipfile.lock

.pypyrc

a11y.json
86 changes: 35 additions & 51 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def test_go_to_google(py):
import os
import shutil
import sys
from pathlib import Path

import pytest
import requests
Expand All @@ -34,34 +35,21 @@ def test_go_to_google(py):
from pylenium.a11y import PyleniumAxe


def make_dir(filepath) -> bool:
""" Make a directory.
Returns:
True if successful, False if not.
"""
try:
os.mkdir(filepath)
return True
except FileExistsError:
return False


@pytest.fixture(scope='function')
def fake() -> Faker:
""" A basic instance of Faker to make test data."""
"""A basic instance of Faker to make test data."""
return Faker()


@pytest.fixture(scope='function')
def api():
""" A basic instance of Requests to make HTTP API calls. """
"""A basic instance of Requests to make HTTP API calls."""
return requests


@pytest.fixture(scope="session")
def rp_logger(request):
""" Report Portal Logger """
"""Report Portal Logger"""
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# Create handler for Report Portal if the service has been
Expand All @@ -83,7 +71,7 @@ def rp_logger(request):

@pytest.fixture(scope='session', autouse=True)
def project_root() -> str:
""" The Project (or Workspace) root as a filepath.
"""The Project (or Workspace) root as a filepath.
* This conftest.py file should be in the Project Root if not already.
"""
Expand All @@ -92,7 +80,7 @@ def project_root() -> str:

@pytest.fixture(scope='session', autouse=True)
def test_run(project_root, request) -> str:
""" Creates the `/test_results` directory to store the results of the Test Run.
"""Creates the `/test_results` directory to store the results of the Test Run.
Returns:
The `/test_results` directory as a filepath (str).
Expand All @@ -103,20 +91,19 @@ def test_run(project_root, request) -> str:
if os.path.exists(test_results_dir):
# delete /test_results from previous Test Run
shutil.rmtree(test_results_dir, ignore_errors=True)
if not os.path.exists(test_results_dir):
# create /test_results for this Test Run
make_dir(test_results_dir)

Path(test_results_dir).mkdir(parents=True, exist_ok=True)

for test in session.items:
# make the test_result directory for each test
make_dir(f'{test_results_dir}/{test.name}')
Path(f'{test_results_dir}/{test.name}').mkdir(parents=True, exist_ok=True)

return test_results_dir


@pytest.fixture(scope='session')
def py_config(project_root, request) -> PyleniumConfig:
""" Initialize a PyleniumConfig for each test
"""Initialize a PyleniumConfig for each test
1. This starts by deserializing the user-created pylenium.json from the Project Root.
2. If that file is not found, then proceed with Pylenium Defaults.
Expand Down Expand Up @@ -174,7 +161,7 @@ def py_config(project_root, request) -> PyleniumConfig:

@pytest.fixture(scope='function')
def test_case(test_run, py_config, request) -> TestCase:
""" Manages data pertaining to the currently running Test Function or Case.
"""Manages data pertaining to the currently running Test Function or Case.
* Creates the test-specific logger.
Expand All @@ -192,7 +179,7 @@ def test_case(test_run, py_config, request) -> TestCase:

@pytest.fixture(scope='function')
def py(test_case, py_config, request, rp_logger):
""" Initialize a Pylenium driver for each test.
"""Initialize a Pylenium driver for each test.
Pass in this `py` fixture into the test function.
Expand All @@ -209,10 +196,10 @@ def test_go_to_google(py):
if py_config.logging.screenshots_on:
screenshot = py.screenshot(f'{test_case.file_path}/test_failed.png')
with open(screenshot, "rb") as image_file:
rp_logger.info("Test Failed - Attaching Screenshot",
attachment={"name": "test_failed.png",
"data": image_file,
"mime": "image/png"})
rp_logger.info(
"Test Failed - Attaching Screenshot",
attachment={"name": "test_failed.png", "data": image_file, "mime": "image/png"},
)
except AttributeError:
rp_logger.error('Unable to access request.node.report.failed, unable to take screenshot.')
except TypeError:
Expand All @@ -222,13 +209,13 @@ def test_go_to_google(py):

@pytest.fixture(scope='function')
def axe(py) -> PyleniumAxe:
""" The aXe A11y audit tool as a fixture. """
"""The aXe A11y audit tool as a fixture."""
return PyleniumAxe(py.webdriver)


@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
""" Yield each test's outcome so we can handle it in other fixtures. """
"""Yield each test's outcome so we can handle it in other fixtures."""
outcome = yield
report = outcome.get_result()
if report.when == 'call':
Expand All @@ -237,31 +224,28 @@ def pytest_runtest_makereport(item, call):


def pytest_addoption(parser):
parser.addoption('--browser', action='store', default='', help='The lowercase browser name: chrome | firefox')
parser.addoption('--remote_url', action='store', default='', help='Grid URL to connect tests to.')
parser.addoption('--screenshots_on', action='store', default='', help="Should screenshots be saved? true | false")
parser.addoption('--pylog_level', action='store', default='', help="Set the pylog_level: 'off' | 'info' | 'debug'")
parser.addoption(
'--browser', action='store', default='', help='The lowercase browser name: chrome | firefox'
)
parser.addoption(
'--remote_url', action='store', default='', help='Grid URL to connect tests to.'
)
parser.addoption(
'--screenshots_on', action='store', default='', help="Should screenshots be saved? true | false"
)
parser.addoption(
'--pylog_level', action='store', default='', help="Set the pylog_level: 'off' | 'info' | 'debug'"
)
parser.addoption(
'--options', action='store',
default='', help='Comma-separated list of Browser Options. Ex. "headless, incognito"'
'--options',
action='store',
default='',
help='Comma-separated list of Browser Options. Ex. "headless, incognito"',
)
parser.addoption(
'--caps', action='store',
default='', help='List of key-value pairs. Ex. \'{"name": "value", "boolean": true}\''
'--caps',
action='store',
default='',
help='List of key-value pairs. Ex. \'{"name": "value", "boolean": true}\'',
)
parser.addoption(
'--page_load_wait_time', action='store',
default='', help='The amount of time to wait for a page load before raising an error. Default is 0.'
'--page_load_wait_time',
action='store',
default='',
help='The amount of time to wait for a page load before raising an error. Default is 0.',
)
parser.addoption(
'--extensions', action='store',
default='', help='Comma-separated list of extension paths. Ex. "*.crx, *.crx"'
'--extensions', action='store', default='', help='Comma-separated list of extension paths. Ex. "*.crx, *.crx"'
)
86 changes: 35 additions & 51 deletions pylenium/scripts/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def test_go_to_google(py):
import os
import shutil
import sys
from pathlib import Path

import pytest
import requests
Expand All @@ -34,34 +35,21 @@ def test_go_to_google(py):
from pylenium.a11y import PyleniumAxe


def make_dir(filepath) -> bool:
""" Make a directory.
Returns:
True if successful, False if not.
"""
try:
os.mkdir(filepath)
return True
except FileExistsError:
return False


@pytest.fixture(scope='function')
def fake() -> Faker:
""" A basic instance of Faker to make test data."""
"""A basic instance of Faker to make test data."""
return Faker()


@pytest.fixture(scope='function')
def api():
""" A basic instance of Requests to make HTTP API calls. """
"""A basic instance of Requests to make HTTP API calls."""
return requests


@pytest.fixture(scope="session")
def rp_logger(request):
""" Report Portal Logger """
"""Report Portal Logger"""
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# Create handler for Report Portal if the service has been
Expand All @@ -83,7 +71,7 @@ def rp_logger(request):

@pytest.fixture(scope='session', autouse=True)
def project_root() -> str:
""" The Project (or Workspace) root as a filepath.
"""The Project (or Workspace) root as a filepath.
* This conftest.py file should be in the Project Root if not already.
"""
Expand All @@ -92,7 +80,7 @@ def project_root() -> str:

@pytest.fixture(scope='session', autouse=True)
def test_run(project_root, request) -> str:
""" Creates the `/test_results` directory to store the results of the Test Run.
"""Creates the `/test_results` directory to store the results of the Test Run.
Returns:
The `/test_results` directory as a filepath (str).
Expand All @@ -103,20 +91,19 @@ def test_run(project_root, request) -> str:
if os.path.exists(test_results_dir):
# delete /test_results from previous Test Run
shutil.rmtree(test_results_dir, ignore_errors=True)
if not os.path.exists(test_results_dir):
# create /test_results for this Test Run
make_dir(test_results_dir)

Path(test_results_dir).mkdir(parents=True, exist_ok=True)

for test in session.items:
# make the test_result directory for each test
make_dir(f'{test_results_dir}/{test.name}')
Path(f'{test_results_dir}/{test.name}').mkdir(parents=True, exist_ok=True)

return test_results_dir


@pytest.fixture(scope='session')
def py_config(project_root, request) -> PyleniumConfig:
""" Initialize a PyleniumConfig for each test
"""Initialize a PyleniumConfig for each test
1. This starts by deserializing the user-created pylenium.json from the Project Root.
2. If that file is not found, then proceed with Pylenium Defaults.
Expand Down Expand Up @@ -174,7 +161,7 @@ def py_config(project_root, request) -> PyleniumConfig:

@pytest.fixture(scope='function')
def test_case(test_run, py_config, request) -> TestCase:
""" Manages data pertaining to the currently running Test Function or Case.
"""Manages data pertaining to the currently running Test Function or Case.
* Creates the test-specific logger.
Expand All @@ -192,7 +179,7 @@ def test_case(test_run, py_config, request) -> TestCase:

@pytest.fixture(scope='function')
def py(test_case, py_config, request, rp_logger):
""" Initialize a Pylenium driver for each test.
"""Initialize a Pylenium driver for each test.
Pass in this `py` fixture into the test function.
Expand All @@ -209,10 +196,10 @@ def test_go_to_google(py):
if py_config.logging.screenshots_on:
screenshot = py.screenshot(f'{test_case.file_path}/test_failed.png')
with open(screenshot, "rb") as image_file:
rp_logger.info("Test Failed - Attaching Screenshot",
attachment={"name": "test_failed.png",
"data": image_file,
"mime": "image/png"})
rp_logger.info(
"Test Failed - Attaching Screenshot",
attachment={"name": "test_failed.png", "data": image_file, "mime": "image/png"},
)
except AttributeError:
rp_logger.error('Unable to access request.node.report.failed, unable to take screenshot.')
except TypeError:
Expand All @@ -222,13 +209,13 @@ def test_go_to_google(py):

@pytest.fixture(scope='function')
def axe(py) -> PyleniumAxe:
""" The aXe A11y audit tool as a fixture. """
"""The aXe A11y audit tool as a fixture."""
return PyleniumAxe(py.webdriver)


@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
""" Yield each test's outcome so we can handle it in other fixtures. """
"""Yield each test's outcome so we can handle it in other fixtures."""
outcome = yield
report = outcome.get_result()
if report.when == 'call':
Expand All @@ -237,31 +224,28 @@ def pytest_runtest_makereport(item, call):


def pytest_addoption(parser):
parser.addoption('--browser', action='store', default='', help='The lowercase browser name: chrome | firefox')
parser.addoption('--remote_url', action='store', default='', help='Grid URL to connect tests to.')
parser.addoption('--screenshots_on', action='store', default='', help="Should screenshots be saved? true | false")
parser.addoption('--pylog_level', action='store', default='', help="Set the pylog_level: 'off' | 'info' | 'debug'")
parser.addoption(
'--browser', action='store', default='', help='The lowercase browser name: chrome | firefox'
)
parser.addoption(
'--remote_url', action='store', default='', help='Grid URL to connect tests to.'
)
parser.addoption(
'--screenshots_on', action='store', default='', help="Should screenshots be saved? true | false"
)
parser.addoption(
'--pylog_level', action='store', default='', help="Set the pylog_level: 'off' | 'info' | 'debug'"
)
parser.addoption(
'--options', action='store',
default='', help='Comma-separated list of Browser Options. Ex. "headless, incognito"'
'--options',
action='store',
default='',
help='Comma-separated list of Browser Options. Ex. "headless, incognito"',
)
parser.addoption(
'--caps', action='store',
default='', help='List of key-value pairs. Ex. \'{"name": "value", "boolean": true}\''
'--caps',
action='store',
default='',
help='List of key-value pairs. Ex. \'{"name": "value", "boolean": true}\'',
)
parser.addoption(
'--page_load_wait_time', action='store',
default='', help='The amount of time to wait for a page load before raising an error. Default is 0.'
'--page_load_wait_time',
action='store',
default='',
help='The amount of time to wait for a page load before raising an error. Default is 0.',
)
parser.addoption(
'--extensions', action='store',
default='', help='Comma-separated list of extension paths. Ex. "*.crx, *.crx"'
'--extensions', action='store', default='', help='Comma-separated list of extension paths. Ex. "*.crx, *.crx"'
)
Loading

0 comments on commit 3f764d5

Please sign in to comment.