Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/news/81.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The backend test suite runs ~8.5x faster (7:27 -> 0:53): the Plone test layers stay alive for the whole pytest session instead of being rebuilt per test class, and content creation plus the Solr query of a parametrized test class run once per class instead of once per assertion. @reebalazs
41 changes: 40 additions & 1 deletion backend/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,45 @@
)


@pytest.fixture(scope="class")
def functional_class_bracket(functional_class):
"""A class-scoped test bracket on the functional layer.

Runs the layer's testSetUp/testTearDown once per test *class*
instead of once per test function, so expensive per-class fixtures
(content creation, a shared query) can be set up a single time and
shared by all tests of the class - the pattern the old
zope.testrunner layers provided. plone.testing layer resources are
stacked (LIFO), so per-function brackets can still nest inside if
a test also uses the function-scoped ``functional`` fixture.

Tests of a class using this bracket share one ZODB/Solr state:
suitable for the common read-only pattern (create content, run a
query, assert many times); not for tests that mutate content.
"""
layer = functional_class
layer.testSetUp()
yield layer
layer.testTearDown()


@pytest.fixture(scope="session", autouse=True)
def keep_zope_layers(functional_session, integration_session):
"""Keep the expensive Plone test layers alive for the whole session.

zope.pytestlayer only preserves layers across test classes for
zope.testrunner style tests (with a ``layer`` class attribute);
for pytest style tests its class-scoped layer fixture tears the
whole layer stack down after every test class, so the Plone site
got rebuilt many times per run - the main reason the suite became
much slower after the unittest to pytest migration. Depending on
the session-scoped layer fixtures marks the layers as
keep-for-whole-session: they are set up once and torn down at the
end of the session. Per-test isolation (testSetUp/testTearDown)
is unaffected.
"""


def is_responsive(url):
"""Helper fixture to check if Solr is up and running."""
try:
Expand Down Expand Up @@ -51,7 +90,7 @@ def docker_compose_file(pytestconfig):
return repo_root / "docker-compose-dev.yml"


@pytest.fixture
@pytest.fixture(scope="session")
def solr_service(docker_ip, docker_services):
"""Ensure that Solr service is up and responsive."""
port = docker_services.port_for("solr-acceptance", 8983)
Expand Down
32 changes: 16 additions & 16 deletions backend/tests/services/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
import transaction


@pytest.fixture
@pytest.fixture(scope="class")
def users() -> list:
"""Additional users to be created."""
return []


@pytest.fixture
@pytest.fixture(scope="class")
def contents() -> list:
"""Content to be created."""
return [
Expand Down Expand Up @@ -61,7 +61,7 @@ def func(data: dict) -> list[str]:
return func


@pytest.fixture
@pytest.fixture(scope="class")
def create_contents(contents):
"""Helper fixture to create initial content."""

Expand Down Expand Up @@ -96,23 +96,23 @@ def func(portal) -> dict:
return func


@pytest.fixture()
def app(functional):
return functional["app"]
@pytest.fixture(scope="class")
def app(functional_class_bracket):
return functional_class_bracket["app"]


@pytest.fixture()
def http_request(functional):
return functional["request"]
@pytest.fixture(scope="class")
def http_request(functional_class_bracket):
return functional_class_bracket["request"]


@pytest.fixture()
@pytest.fixture(scope="class")
def registry_config() -> dict:
"""Fixture with plone.app.registry settings."""
return {"collective.solr.active": 1}


@pytest.fixture()
@pytest.fixture(scope="class")
def portal(app, solr_service, http_request, users, registry_config):
"""Plone portal with additional users, and registry configuration set."""
portal = app["plone"]
Expand All @@ -135,7 +135,7 @@ def portal(app, solr_service, http_request, users, registry_config):
transaction.commit()


@pytest.fixture()
@pytest.fixture(scope="class")
def portal_with_content(app, portal, create_contents):
"""Plone portal with initial content."""
with api.env.adopt_roles(["Manager"]):
Expand All @@ -150,15 +150,15 @@ def portal_with_content(app, portal, create_contents):
transaction.commit()


@pytest.fixture()
@pytest.fixture(scope="class")
def maintenance(portal, http_request):
"""Return browser view for solr maintenance."""
with api.env.adopt_roles(["Manager"]):
view = api.content.get_view("solr-maintenance", portal, http_request)
return view


@pytest.fixture()
@pytest.fixture(scope="class")
def request_factory(portal):
"""Fixture returning a session to call the API."""

Expand All @@ -171,13 +171,13 @@ def factory() -> RelativeSession:
return factory


@pytest.fixture()
@pytest.fixture(scope="class")
def anon_request(request_factory):
"""Anonymous API requests."""
return request_factory()


@pytest.fixture()
@pytest.fixture(scope="class")
def manager_request(request_factory):
"""Manager API requests."""
request = request_factory()
Expand Down
2 changes: 1 addition & 1 deletion backend/tests/services/content_filters/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest


@pytest.fixture
@pytest.fixture(scope="class")
def contents() -> list:
return [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
class TestEndpointContentFields:
url: str = "@solr"

@pytest.fixture(autouse=True)
def _init(self, portal_with_content, manager_request):
self.portal = portal_with_content
response = manager_request.get(self.url)
self.data = response.json()
@pytest.fixture(autouse=True, scope="class")
def _init(self, request, portal_with_content, manager_request):
request.cls.portal = portal_with_content
response = manager_request.get(request.cls.url)
request.cls.data = response.json()


class TestEndpointContentFieldsId(TestEndpointContentFields):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@


class TestEndpointPortalType:
@pytest.fixture(autouse=True)
def _init(self, portal_with_content, manager_request):
self.portal = portal_with_content
response = manager_request.get(self.url)
self.data = response.json()
@pytest.fixture(autouse=True, scope="class")
def _init(self, request, portal_with_content, manager_request):
request.cls.portal = portal_with_content
response = manager_request.get(request.cls.url)
request.cls.data = response.json()


class TestEndpointPortalTypeNotPassed(TestEndpointPortalType):
Expand Down
12 changes: 6 additions & 6 deletions backend/tests/services/custom_config/test_endpoint_custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
}


@pytest.fixture()
@pytest.fixture(scope="class")
def registry_config() -> dict:
"""Override registry configuration."""
return {
Expand All @@ -47,11 +47,11 @@ def registry_config() -> dict:


class TestEndpointCustom:
@pytest.fixture(autouse=True)
def _init(self, portal_with_content, manager_request):
self.portal = portal_with_content
response = manager_request.get(self.url)
self.data = response.json()
@pytest.fixture(autouse=True, scope="class")
def _init(self, request, portal_with_content, manager_request):
request.cls.portal = portal_with_content
response = manager_request.get(request.cls.url)
request.cls.data = response.json()


class TestEndpointCustomBaseSearch(TestEndpointCustom):
Expand Down
12 changes: 6 additions & 6 deletions backend/tests/services/custom_config/test_endpoint_first_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
}


@pytest.fixture()
@pytest.fixture(scope="class")
def registry_config() -> dict:
"""Override registry configuration."""
return {
Expand All @@ -47,11 +47,11 @@ def registry_config() -> dict:


class TestEndpointFirstTab:
@pytest.fixture(autouse=True)
def _init(self, portal_with_content, manager_request):
self.portal = portal_with_content
response = manager_request.get(self.url)
self.data = response.json()
@pytest.fixture(autouse=True, scope="class")
def _init(self, request, portal_with_content, manager_request):
request.cls.portal = portal_with_content
response = manager_request.get(request.cls.url)
request.cls.data = response.json()


class TestEndpointFirstTabBaseSearch(TestEndpointFirstTab):
Expand Down
10 changes: 5 additions & 5 deletions backend/tests/services/default_config/test_endpoint_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@


class TestEndpointDefault:
@pytest.fixture(autouse=True)
def _init(self, portal_with_content, manager_request):
self.portal = portal_with_content
response = manager_request.get(self.url)
self.data = response.json()
@pytest.fixture(autouse=True, scope="class")
def _init(self, request, portal_with_content, manager_request):
request.cls.portal = portal_with_content
response = manager_request.get(request.cls.url)
request.cls.data = response.json()


class TestEndpointDefaultBaseSearch(TestEndpointDefault):
Expand Down
2 changes: 1 addition & 1 deletion backend/tests/services/encoding/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest


@pytest.fixture
@pytest.fixture(scope="class")
def contents() -> list:
return [
{
Expand Down
10 changes: 5 additions & 5 deletions backend/tests/services/encoding/test_endpoint_encoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@


class TestEndpointEncoding:
@pytest.fixture(autouse=True)
def _init(self, portal_with_content, manager_request):
self.portal = portal_with_content
response = manager_request.get(self.url)
self.data = response.json()
@pytest.fixture(autouse=True, scope="class")
def _init(self, request, portal_with_content, manager_request):
request.cls.portal = portal_with_content
response = manager_request.get(request.cls.url)
request.cls.data = response.json()


class TestEndpointEncodingColon(TestEndpointEncoding):
Expand Down
2 changes: 1 addition & 1 deletion backend/tests/services/extra_conditions/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest


@pytest.fixture
@pytest.fixture(scope="class")
def contents() -> list:
return [
{
Expand Down
12 changes: 6 additions & 6 deletions backend/tests/services/extra_conditions/test_extra_conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def encoded(o):
}


@pytest.fixture()
@pytest.fixture(scope="class")
def registry_config() -> dict:
"""Override registry configuration."""
return {
Expand All @@ -62,11 +62,11 @@ def registry_config() -> dict:


class TestEndpointCustom:
@pytest.fixture(autouse=True)
def _init(self, portal_with_content, manager_request):
self.portal = portal_with_content
response = manager_request.get(self.url)
self.data = response.json()
@pytest.fixture(autouse=True, scope="class")
def _init(self, request, portal_with_content, manager_request):
request.cls.portal = portal_with_content
response = manager_request.get(request.cls.url)
request.cls.data = response.json()


class TestExtraConditionsInactive(TestEndpointCustom):
Expand Down
2 changes: 1 addition & 1 deletion backend/tests/services/facet_conditions/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest


@pytest.fixture
@pytest.fixture(scope="class")
def contents() -> list:
return [
{
Expand Down
14 changes: 8 additions & 6 deletions backend/tests/services/facet_conditions/test_facet_conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def encoded(o):
}


@pytest.fixture()
@pytest.fixture(scope="class")
def registry_config() -> dict:
"""Override registry configuration."""
return {
Expand All @@ -60,11 +60,13 @@ def registry_config() -> dict:


class TestEndpointCustom:
@pytest.fixture(autouse=True)
def _init(self, portal_with_content, manager_request):
self.portal = portal_with_content
response = manager_request.get(self.url)
self.data = response.json()
@pytest.fixture(autouse=True, scope="class")
def _init(self, request, portal_with_content, manager_request):
request.cls.portal = portal_with_content
# url can be an instance property (derived per class); evaluate
# it on a throwaway instance
response = manager_request.get(request.cls().url)
request.cls.data = response.json()


class TestFacetConditionsInactive(TestEndpointCustom):
Expand Down
2 changes: 1 addition & 1 deletion backend/tests/services/highlighting/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest


@pytest.fixture
@pytest.fixture(scope="class")
def contents() -> list:
"""Content to be created."""
return [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@


class TestEndpointDefault:
@pytest.fixture(autouse=True)
def _init(self, portal_with_content, manager_request):
self.portal = portal_with_content
response = manager_request.get(self.url)
self.data = response.json()
@pytest.fixture(autouse=True, scope="class")
def _init(self, request, portal_with_content, manager_request):
request.cls.portal = portal_with_content
response = manager_request.get(request.cls.url)
request.cls.data = response.json()

def func(data: dict) -> list[str]:
return [item["path_string"] for item in data["response"]["docs"]]
Expand Down
Loading
Loading