Is there way to show test author name in pytest-html reports for each tests? - pytest

Is there way to show test author name in pytest-html reports for each tests ?

Yes. You can do that. Put __author__ attribute in each test module.
# test_add.py
__author__ = 'Vivek R'
def add(x, y):
return x + y
def test_add():
assert add(5, 4) == 9
And call some fixtures in conftest.py file.
# conftest.py
from py.xml import html
import pytest
def pytest_html_results_table_header(cells):
cells.append(html.th("Author"))
def pytest_html_results_table_row(report, cells):
cells.append(html.td(report.author))
#pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
report.author = item.module.__author__

Related

Dialog button click in test not dismissing dialog PyQt5/pytest

When running a test that launches a dialog, the test is unable to programmatically click a button on the dialog. If I manually click the button, the test completes. Also I'm unsure why the dialog even shows at all (since I don't see the original window it was launched from).
Python 3.7.16, pytest 7.2.1
foo.py
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Foo(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setupUi()
def setupUi(self):
self.resize(400, 200)
self.button = QtWidgets.QPushButton("ClickMe")
self.button.clicked.connect(self.launchDialog)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
def launchDialog(self):
self.dialog = FooDialog(parent=self)
self.dialog.exec()
class FooDialog(QtWidgets.QMessageBox):
def __init__(self, parent=None):
super(FooDialog, self).__init__(parent)
self.setWindowTitle("foo.FooDialog")
self.button = QtWidgets.QPushButton("ClickMe")
self.addButton(self.button, QtWidgets.QMessageBox.ButtonRole.ActionRole)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
foo = Foo()
foo.show()
sys.exit(app.exec_())
test_foo.py
import pytest, time
from pytestqt.qtbot import QtBot
from PyQt5 import QtCore
from foo import Foo
#pytest.fixture
def app(qtbot):
foo = Foo()
qtbot.addWidget(foo)
return foo
def test_button(app):
qtbot = QtBot(app)
qtbot.mouseClick(app.button, QtCore.Qt.MouseButton.LeftButton, delay=0)
time.sleep(2)
qtbot.mouseClick(app.dialog.button, QtCore.Qt.MouseButton.LeftButton, delay=0)
I run the test thus:
pytest -s test_foo.py
I ran the test and expected it to complete, but instead the test hangs waiting for the button on the dialog to be clicked.
I worked around the problem by mocking the dialog

pytest - mockup a complex module import

I have found several posts on how to "hide" a package and simulate an ImportError with pytest, however, I haven't succeeded in my case and I am looking for some help:
Test for import of optional dependencies in __init__.py with pytest: Python 3.5 /3.6 differs in behaviour
Test behavior of code if optional module is not installed
and related
Here is the content of an __about__.py file that I want to test with pytest.
"""Get the metadata from the package or from setup.py."""
try:
import importlib
metadata = importlib.metadata
except ImportError:
import importlib_metadata as metadata
try:
data = metadata.metadata("mypackage")
__version__ = data["Version"]
__author__ = data["Author"]
__name__ = data["Name"]
except metadata.PackageNotFoundError:
# The repo of the package is accessible to python to get at least the version
import re
from pathlib import Path
try:
from nested_grid_plotter import __file__ as loc
with open(Path(loc).parent.joinpath("../setup.py"), "r") as f:
data = f.read()
except FileNotFoundError:
data = ""
def version_parser(v):
"""Parse the version from the setup file."""
version_pattern = (
r"""(version\s*=\s*)["|'](\d+(=?\.(\d+(=?\.(\d+)*)*)*)*)["|']"""
)
regex_matcher = re.compile(version_pattern).search(v)
if regex_matcher is None:
return "unknwon"
return regex_matcher.group(2)
try:
__version__ = version_parser(data)
except Exception:
__version__ = "unknown"
__author__ = "unknown"
__name__ = "unknown"
Here is the __init__.py at the root of the package:
from .__about__ import __version__, __name__, __author__
And here is the tests that I have come up with until now. However, I am not able to hide importlib.
"""Test the file __about__.py."""
import pytest
import sys
class PackageDiscarder:
def __init__(self):
self.pkgnames = []
def find_spec(self, fullname, path, target=None):
if fullname in self.pkgnames:
raise ImportError()
#pytest.fixture
def no_requests():
sys.modules.pop("importlib", None)
d = PackageDiscarder()
d.pkgnames.append("importlib")
sys.meta_path.insert(0, d)
yield
sys.meta_path.remove(d)
#pytest.fixture(autouse=True)
def cleanup_imports():
yield
sys.modules.pop("mypackage", None)
def test_requests_available():
import mypackage
assert mypackage.__version__ != "unknwon"
#pytest.mark.usefixtures("no_requests")
def test_requests_missing():
import mypackage
assert mypackage.__version__ != "unknwon"
Here is the coverage report:
Name Stmts Miss Cover Missing
----------------------------------------------------------------
mypackage/__about__.py 31 10 68% 5-6, 10-12, 23-24, 33, 38-39
----------------------------------------------------------------
TOTAL 31 10 68%

Copy contents of tmp_path -> tmp_path_factory for use in the next test

Test_1 writes out a file tree to the standard tmp_path fixture.
Test_2 requires this file tree as the input to its own test.
I am using shutil.copytree() however I am getting an error:
import pytest
import shutil
import os
def test_1(tmp_path, tmp_path_factory):
p = tmp_path / "me"
shutil.copytree(p, tmp_path_factory, dirs_exist_ok=True)
assert True
def test_2(tmp_path_factory):
l = list(tmp_path_factory.iterdir())
print(l)
assert False
Error:
p = TempPathFactory(_given_basetemp=None, _trace=<pluggy._tracing.TagTracerSub object at 0x000002161F3F4BE0>, _basetemp=WindowsPath('C:/Users/David/AppData/Local/Temp/pytest-of-David/pytest-1125'))
def split(p):
"""Split a pathname.
Return tuple (head, tail) where tail is everything after the final slash.
Either part may be empty."""
> p = os.fspath(p)
E TypeError: expected str, bytes or os.PathLike object, not TempPathFactory
Any ideas? Many thanks :)
Thanks to #hoefling, this works
import pytest
import shutil
from directory_tree import display_tree
#pytest.fixture(scope="session")
def cache_dir(tmp_path_factory):
return tmp_path_factory.mktemp("cache")#, numbered=False)
def test_1(tmp_path, cache_dir):
p = tmp_path / "me.txt"
p.touch()
shutil.copytree(p.parent, cache_dir, dirs_exist_ok=True)
assert True
def test_2(cache_dir):
display_tree(cache_dir)
assert False

pytest with classes python

I wrote the following code :
publisher.py:
import six
from google.api_core.exceptions import AlreadyExists
from google.cloud.pubsub import types
class publisher(object):
"""Publisher Object which has the following attributes
Attributes:
pubsub: publisher client
project_name: Name of project
topic_name: Name of topic
"""
def __init__(self, pubsub, project_name, topic_name, batch_settings=(), *args, **kwargs):
self.pubsub = pubsub
self.project_name = project_name
self.topic_name = topic_name
self.batch_settings = types.BatchSettings(
*batch_settings) # Batch setting Pub/Sub accepts a maximum of 1,000 messages in a batch,
# and the size of a batch can not exceed 10 megabytes
def _full_project_name(self):
"""Returns Fully Qualified Name of project"""
return self.pubsub.project_path(self.project_name)
and I wrote 3 test unfortunately the third one has been failing.
Below is the code I wrote for tests:
test_publisher.py:
from google.cloud import pubsub
import pytest
from publisher import publisher
PROJECT = 'ProjectTest'
TOPIC_NAME = 'TopicTest'
#pytest.fixture
def pubsub():
yield pubsub.PublisherClient()
def test_init_value():
sample_publisher=publisher(pubsub,PROJECT,TOPIC_NAME,())
assert sample_publisher.project_name == 'ProjectTest'
assert sample_publisher.topic_name == 'TopicTest'
assert sample_publisher.pubsub == pubsub
assert sample_publisher.batch_settings.max_messages == 1000
assert sample_publisher.batch_settings.max_bytes == 10 * (2 ** 20)
assert sample_publisher.batch_settings.max_latency == 0.05
def test_init_with_no_values():
with pytest.raises(Exception) as e_info:
sample_bad_init = publisher()
def test_full_project_name ():
sample_publisher = publisher(pubsub, PROJECT, TOPIC_NAME, ())
assert sample_publisher._full_project_name() == 'projects/ProjectTest'
I am currently getting the following error, which I can't understand, unfortunately:
line 26, in _full_project_name
return self.pubsub.project_path(self.project_name)
AttributeError: 'function' object has no attribute 'project_path'
Any help with this, please.
Thanks a lot
The name of fixture should be changed.
#pytest.fixture
def google_pubsub():
yield pubsub.PublisherClient()
You should add google_pubsub as argument to test test_full_project_name(google_pubsub) and test_init_value(google_pubsub).
In Test test_init_value you use module pubsub imported from from google.cloud import pubsub, what is wrong.
Test test_init_value passes because you comparing module(pubsub) in line
assert sample_publisher.pubsub == google_pubsub

Flask unittest and sqlalchemy using all connections

I've just run into an issue running unittests on my flask app after I had roughly 100 unittests. All unittests will pass, but when run all at once they will fail with the following error:
OperationalError: (OperationalError) FATAL: remaining connection slots are reserved for non-replication superuser connections
Everything is running in a virtualbox/vagrant/ubuntu12.04 instance on local machine. My postgres max_connections is set to 100 so I'm assuming that the connections aren't closing and after running 100 tests I use up all the available ones.
This person Flask unit tests with SQLAlchemy and PostgreSQL exhausts db connections looks like they are having the same exact problem. Mike/Zzzeek (sqlalchemy dev) even responded to it saying that something may be happening in create_app() so I've included that as well below.
Does this mean I'm not closing my connections somewhere? All of these errors are triggered by db.create_all() in my setUp() method of my unittest.
# test.py
class TestCase(DataMixin, Base):
"""Base test class"""
def create_app(self):
return create_app(TestConfig())
def setUp(self):
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
# app.py
def create_app(config=None):
app = Flask(__name__)
# Config
app.config.from_object(BaseConfig())
if config is not None:
app.config.from_object(config)
# Extensions
db.init_app(app)
mail.init_app(app)
bcrypt.init_app(app)
# Blueprints
app.register_blueprint(core_blueprint, url_prefix='/')
app.register_blueprint(accounts_blueprint, url_prefix='/account')
app.register_blueprint(admin_blueprint, url_prefix='/admin')
app.register_blueprint(cart_blueprint, url_prefix='/cart')
# Login Manager
login_manager.setup_app(app, add_context_processor=True)
login_manager.login_view = "accounts.login"
login_manager.user_callback = load_user
# Templates
app.jinja_env.globals['is_admin'] = is_admin
app.jinja_env.globals['is_staff'] = is_staff
#app.context_processor
def inject_cart():
cart = count = None
if current_user.is_authenticated():
cart = current_user.get_cart()
return dict(cart=cart)
# Error Handling
#app.errorhandler(404)
def page_not_found(error):
return render_template('404.html'), 404
return app
UPDATE: Tested and fixed
Instead of making a new connection and re-creating your database every time (slow), you can use subsessions and do a rollback after each test.
The connection are reused, so this also fix the problem you're having.
class TestCase(Base):
#classmethod
def setUpClass(cls):
cls.app = create_app(MyConfig())
cls.client = cls.app.test_client()
cls._ctx = cls.app.test_request_context()
cls._ctx.push()
db.create_all()
#classmethod
def tearDownClass(cls):
db.session.remove()
db.drop_all()
db.get_engine(cls.app).dispose()
def setUp(self):
self._ctx = self.app.test_request_context()
self._ctx.push()
db.session.begin(subtransactions=True)
def tearDown(self):
db.session.rollback()
db.session.close()
self._ctx.pop()
If you need to also make an instance of the application for each test, just add it to the setUp method but leave it also in setUpClass.
Full test example below requires flask_sqlalchemy and psycopg2. Create a test database named "test" and set its connection limit to 15.
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from unittest import TestCase as Base
db = SQLAlchemy()
def create_app(config=None):
app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)
return app
class MyConfig(object):
SQLALCHEMY_DATABASE_URI = "postgresql://localhost/test"
TESTING = True
class TestCase(Base):
#classmethod
def setUpClass(cls):
cls.app = create_app(MyConfig())
cls.client = cls.app.test_client()
cls._ctx = cls.app.test_request_context()
cls._ctx.push()
db.create_all()
#classmethod
def tearDownClass(cls):
db.session.remove()
db.drop_all()
def setUp(self):
self._ctx = self.app.test_request_context()
self._ctx.push()
db.session.begin(subtransactions=True)
def tearDown(self):
db.session.rollback()
db.session.close()
self._ctx.pop()
class TestModel(TestCase):
def test_01(self):
pass
def test_02(self):
pass
def test_03(self):
pass
def test_04(self):
pass
def test_05(self):
pass
def test_06(self):
pass
def test_07(self):
pass
def test_08(self):
pass
def test_09(self):
pass
def test_10(self):
pass
def test_11(self):
pass
def test_12(self):
pass
def test_13(self):
pass
def test_14(self):
pass
def test_15(self):
pass
def test_16(self):
pass
if __name__ == "__main__":
import unittest
unittest.main()
I found the answer here -- https://stackoverflow.com/a/17998485/1870623 and a great explination here -- https://stackoverflow.com/a/16390645/1870623
The solution is to add db.get_engine(self.app).dispose() to the tearDown()
class TestCase(Base):
def setUp(self):
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
db.get_engine(self.app).dispose() # This