Skip to content

Commit b948331

Browse files
author
Pavel Papou
committed
Pytest-Reportportal client
1 parent 0e2e1e3 commit b948331

File tree

7 files changed

+381
-0
lines changed

7 files changed

+381
-0
lines changed

CONTRIBUTING.rst

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
.. highlight:: shell
2+
3+
============
4+
Contributing
5+
============
6+
7+
Contributions are welcome, and they are greatly appreciated! Every
8+
little bit helps, and credit will always be given.
9+
10+
You can contribute in many ways:
11+
12+
Types of Contributions
13+
----------------------
14+
15+
Report Bugs
16+
~~~~~~~~~~~
17+
18+
Report bugs at https://github.com/reportportal/agent-python-pytest/issues.
19+
20+
If you are reporting a bug, please include:
21+
22+
* Your operating system name and version.
23+
* Any details about your local setup that might be helpful in troubleshooting.
24+
* Detailed steps to reproduce the bug.
25+
26+
Fix Bugs
27+
~~~~~~~~
28+
29+
Look through the GitHub issues for bugs. Anything tagged with "bug"
30+
and "help wanted" is open to whoever wants to implement it.
31+
32+
Implement Features
33+
~~~~~~~~~~~~~~~~~~
34+
35+
Look through the GitHub issues for features. Anything tagged with "enhancement"
36+
and "help wanted" is open to whoever wants to implement it.
37+
38+
Write Documentation
39+
~~~~~~~~~~~~~~~~~~~
40+
41+
agent-python-pytest could always use more documentation, whether as part of the
42+
official agent-python-pytest docs, in docstrings, or even on the web in blog posts,
43+
articles, and such.
44+
45+
Submit Feedback
46+
~~~~~~~~~~~~~~~
47+
48+
The best way to send feedback is to file an issue at https://github.com/reportportal/agent-python-pytest/issues.
49+
50+
If you are proposing a feature:
51+
52+
* Explain in detail how it would work.
53+
* Keep the scope as narrow as possible, to make it easier to implement.
54+
* Remember that this is a volunteer-driven project, and that contributions
55+
are welcome :)
56+
57+
Get Started!
58+
------------
59+
60+
Ready to contribute? Here's how to set up `agent-python-pytest` for local development.
61+
62+
1. Fork the `agent-python-pytest` repo on GitHub.
63+
2. Clone your fork locally::
64+
65+
$ git clone [email protected]:your_name_here/agent-python-pytest
66+
67+
3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development::
68+
69+
$ mkvirtualenv agent_python_pytest
70+
$ cd agent_python_pytest/
71+
$ python setup.py develop
72+
73+
4. Create a branch for local development::
74+
75+
$ git checkout -b name-of-your-bugfix-or-feature
76+
77+
Now you can make your changes locally.
78+
79+
5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox::
80+
81+
$ flake8 agent_python_pytest tests
82+
$ tox
83+
84+
To get flake8 and tox, just pip install them into your virtualenv.
85+
86+
6. Commit your changes and push your branch to GitHub::
87+
88+
$ git add .
89+
$ git commit -m "Your detailed description of your changes."
90+
$ git push origin name-of-your-bugfix-or-feature
91+
92+
7. Submit a pull request through the GitHub website.
93+

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,24 @@
11
# agent-python-pytest
22
Framework integration with PyTest
3+
4+
Description:
5+
Plugin for reporting items results of Pytest to the 'Reportal Portal'.
6+
7+
Install Plugin
8+
pip install pytest-reportportal
9+
10+
Usage:
11+
12+
Prepare the config file pytest.ini in root dir of tests.
13+
Content of the pytest.ini file:
14+
[pytest]
15+
rp_uuid = uid reportportal
16+
rp_endpoint = http://ip:port
17+
rp_project = Project of ReportPortal
18+
19+
Run tests:
20+
py.test ./tests --rp_launch AnyLaunchName
21+
22+
Features:
23+
- add logging
24+
- add Tags parameter to config file

pytest_reportportal/__init__.py

Whitespace-only changes.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import pytest
2+
import logging
3+
from service import PyTestService
4+
5+
6+
class RP_Report_Listener(object):
7+
8+
# Identifier if TestItem is called:
9+
# if setup is failed, pytest will NOT call
10+
# TestItem and Result will not reported!
11+
called = None
12+
# Test Item result
13+
result = None
14+
15+
@pytest.mark.hookwrapper
16+
def pytest_runtest_makereport(self, item, call):
17+
report = (yield).get_result()
18+
if report.when == "setup":
19+
# when function pytest_setup is called,
20+
# test item session is started in RP
21+
PyTestService.start_pytest_item(item)
22+
23+
if report.when == "call":
24+
self.called = True
25+
if report.passed:
26+
item_result = "PASSED"
27+
elif report.failed:
28+
item_result = "FAILED"
29+
else:
30+
item_result = "SKIPPED"
31+
32+
self.result = item_result
33+
34+
if report.when == "teardown":
35+
# If item is called, result of TestItem is reported
36+
if self.called is True:
37+
item_result = self.result
38+
else:
39+
# If setup - failed or skipped,
40+
# the TestItem will reported as SKIPPED
41+
item_result = "SKIPPED"
42+
PyTestService.finish_pytest_item(item_result)
43+
44+
45+
def pytest_sessionstart(session):
46+
config = session.config
47+
if config.option.rp_launch:
48+
# get config parameters if rp_launch option is set
49+
rp_uuid = config.getini('rp_uuid')
50+
rp_project = config.getini('rp_project')
51+
rp_endpoint = config.getini('rp_endpoint')
52+
# initialize PyTest
53+
PyTestService.init_service(
54+
project=rp_project,
55+
endpoint=rp_endpoint,
56+
uuid=rp_uuid)
57+
launch_name = config.getoption('rp_launch')
58+
59+
PyTestService.start_launch(launch_name)
60+
61+
62+
def pytest_sessionfinish(session):
63+
config = session.config
64+
if config.option.rp_launch:
65+
PyTestService.finish_launch("RP_Launch")
66+
67+
68+
def pytest_configure(config):
69+
rp_launch = config.getoption('rp_launch')
70+
if rp_launch:
71+
# set Pytest_Reporter and configure it
72+
config._reporter = RP_Report_Listener()
73+
74+
if hasattr(config, '_reporter'):
75+
config.pluginmanager.register(config._reporter)
76+
77+
78+
def pytest_unconfigure(config):
79+
if hasattr(config, '_reporter'):
80+
reporter = config._reporter
81+
del config._reporter
82+
config.pluginmanager.unregister(reporter)
83+
logging.debug('ReportPortal is unconfigured')
84+
85+
86+
def pytest_addoption(parser):
87+
group = parser.getgroup("reporting")
88+
group.addoption(
89+
'--rp_launch',
90+
action='store',
91+
dest=None,
92+
help="The Launch name for ReportPortal, i.e PyTest")
93+
94+
parser.addini(
95+
'rp_uuid',
96+
help="Uid of RP user")
97+
98+
parser.addini(
99+
'rp_endpoint',
100+
help="Report portal server")
101+
102+
parser.addini(
103+
"rp_project",
104+
help='Report Portal Project')

pytest_reportportal/service.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import logging
2+
from time import time
3+
from reportportal_client import (
4+
ReportPortalService, FinishExecutionRQ, StartLaunchRQ, StartTestItemRQ,
5+
FinishTestItemRQ)
6+
7+
8+
def timestamp():
9+
return str(int(time() * 1000))
10+
11+
12+
class PyTestService(object):
13+
14+
RP = None
15+
TEST_ITEM_STACK = []
16+
launch_id = None
17+
# TODO: Implement and investigate the logging in PyTest
18+
19+
@staticmethod
20+
def init_service(endpoint, project, uuid):
21+
22+
if PyTestService.RP is None:
23+
logging.debug(
24+
msg="ReportPortal - Init service: "
25+
"endpoint={0}, project={1}, uuid={2}".
26+
format(endpoint, project, uuid))
27+
PyTestService.RP = ReportPortalService(
28+
endpoint=endpoint,
29+
project=project,
30+
token=uuid)
31+
else:
32+
raise Exception("PyTest is initialized")
33+
return PyTestService.RP
34+
35+
@staticmethod
36+
def start_launch(launch_name=None, mode=None):
37+
# TODO: Tags, values could be moved to separated module like helper
38+
tags = ["PyTest"]
39+
sl_pt = StartLaunchRQ(
40+
name=launch_name,
41+
start_time=timestamp(),
42+
description="PyTest_Launch",
43+
mode=mode,
44+
tags=tags)
45+
logging.debug(msg="ReportPortal - Start launch: "
46+
"request_body={0}".format(sl_pt.data))
47+
req_data = PyTestService.RP.start_launch(sl_pt)
48+
logging.debug(msg="ReportPortal - Launch started: "
49+
"response_body={0}".format(req_data.raw))
50+
PyTestService.launch_id = req_data.id
51+
52+
PyTestService.TEST_ITEM_STACK.append((None, "SUITE"))
53+
logging.debug(
54+
msg="ReportPortal - Stack: {0}".
55+
format(PyTestService.TEST_ITEM_STACK))
56+
57+
@staticmethod
58+
def start_pytest_item(test_item=None):
59+
try:
60+
# for common items
61+
item_description = test_item.function.__doc__
62+
except AttributeError:
63+
# doctest has no `function` attribute
64+
item_description = test_item.reportinfo()[2]
65+
66+
start_rq = StartTestItemRQ(
67+
name=test_item.name,
68+
description=item_description,
69+
tags=['PyTest Item Tag'],
70+
start_time=timestamp(),
71+
launch_id=PyTestService.launch_id,
72+
type="TEST")
73+
74+
parent_item_id = PyTestService._get_top_id_from_stack()
75+
76+
logging.debug(
77+
msg="ReportPortal - Start TestItem: "
78+
"request_body={0}, parent_item={1}".format(
79+
start_rq.data, parent_item_id))
80+
81+
req_data = PyTestService.RP.start_test_item(
82+
parent_item_id=parent_item_id, start_test_item_rq=start_rq)
83+
84+
PyTestService.TEST_ITEM_STACK.append((req_data.id, "TEST"))
85+
logging.debug(
86+
msg="ReportPortal - Stack: {0}".
87+
format(PyTestService.TEST_ITEM_STACK))
88+
89+
@staticmethod
90+
def finish_pytest_item(status, issue=None):
91+
fta_rq = FinishTestItemRQ(end_time=timestamp(),
92+
status=status,
93+
issue=issue)
94+
95+
test_item_id = PyTestService._get_top_id_from_stack()
96+
logging.debug(
97+
msg="ReportPortal - Finish TetsItem:"
98+
" request_body={0}, test_id={1}".
99+
format(fta_rq.data, test_item_id))
100+
PyTestService.RP.finish_test_item(
101+
item_id=test_item_id,
102+
finish_test_item_rq=fta_rq)
103+
PyTestService.TEST_ITEM_STACK.pop()
104+
logging.debug(
105+
msg="ReportPortal - Stack: {0}".
106+
format(PyTestService.TEST_ITEM_STACK))
107+
108+
@staticmethod
109+
def finish_launch(status):
110+
fl_rq = FinishExecutionRQ(
111+
end_time=timestamp(),
112+
status=status)
113+
launch_id = PyTestService.launch_id
114+
logging.debug(msg="ReportPortal - Finish launch: "
115+
"request_body={0}, launch_id={1}".format(fl_rq.data,
116+
launch_id))
117+
PyTestService.RP.finish_launch(launch_id, fl_rq)
118+
PyTestService.TEST_ITEM_STACK.pop()
119+
logging.debug(
120+
msg="ReportPortal - Stack: {0}".
121+
format(PyTestService.TEST_ITEM_STACK))
122+
123+
@staticmethod
124+
def _get_top_id_from_stack():
125+
try:
126+
return PyTestService.TEST_ITEM_STACK[-1][0]
127+
except IndexError:
128+
return None

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[metadata]
2+
description-file = README.md

setup.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from setuptools import setup
2+
3+
with open('README.md') as readme_file:
4+
readme = readme_file.read()
5+
6+
version = '0.2.1'
7+
requirements = [
8+
"reportportal_client"
9+
]
10+
11+
setup(
12+
name='pytest-reportportal',
13+
version=version,
14+
description="Framework integration with PyTest",
15+
long_description=readme + '\n\n',
16+
author="Pavel Papou",
17+
author_email='[email protected]',
18+
url='https://github.com/reportportal/agent-python-pytest',
19+
packages=['pytest_reportportal'],
20+
install_requires=requirements,
21+
license="GNU General Public License v3",
22+
keywords=['testing', 'reporting', 'reportportal', 'pytest'],
23+
classifiers=[
24+
"Programming Language :: Python :: 2",
25+
'Programming Language :: Python :: 2.6',
26+
'Programming Language :: Python :: 2.7'],
27+
entry_points={
28+
'pytest11': [
29+
'pytest_reportportal = pytest_reportportal.pytest_rp_plugin',
30+
]
31+
},
32+
)

0 commit comments

Comments
 (0)