Skip to content

Use pytest-xdist to run tests #2504

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft

Use pytest-xdist to run tests #2504

wants to merge 3 commits into from

Conversation

StevenMaude
Copy link
Contributor

@StevenMaude StevenMaude commented May 7, 2025

Depends on #2495, and should be rebased when #2495 is merged.

It's possible to run the tests with pytest-xdist since each test is now independently runnable. This makes the unit tests run about 10 seconds or so quicker in CI (about 20-30% faster over not running with pytest-xdist). I see a similar improvement locally.

@StevenMaude StevenMaude force-pushed the steve/pytest-xdist branch 2 times, most recently from 7eabf7e to 6e2c276 Compare May 7, 2025 18:11
@StevenMaude
Copy link
Contributor Author

StevenMaude commented May 7, 2025

This might need some checking with Playwright too, though in principle we should make those tests run independently too. (Playwright supports xdist; it's a question of us writing our tests to also on separate workers without any interdependencies.)

For now, it seems that the benefit is with at most two workers, since each
worker needs all the `universe` fixture setup.

It may improve if we can break out tests into needing specific, finer
grained fixtures. So we could revisit this in future.
@StevenMaude StevenMaude force-pushed the steve/pytest-xdist branch from 8427bd8 to 6414fca Compare May 13, 2025 15:09
@StevenMaude
Copy link
Contributor Author

StevenMaude commented May 13, 2025

I tried this out with the Playwright tests, and it's highlighted something slightly odd there.

They mostly work, but the test_build_bnf_codelist_single_search doesn't seem to work when run with two workers (but does when run with one):

Traceback (most recent call last):
  File "…venv/opencodelists-3.12/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "…venv/opencodelists-3.12/lib/python3.12/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "…venv/opencodelists-3.12/lib/python3.12/site-packages/django/contrib/auth/decorators.py", line 60, in _view_wrapper
    return view_func(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "…local/src/github.com/opensafely-core/opencodelists/opencodelists/views/user_create_codelist.py", line 27, in user_create_codelist
    return handle_post(request, user, owner_choices)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "…local/src/github.com/opensafely-core/opencodelists/opencodelists/views/user_create_codelist.py", line 43, in handle_post
    return handle_post_valid(request, form, user, owner_choices)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "…local/src/github.com/opensafely-core/opencodelists/opencodelists/views/user_create_codelist.py", line 82, in handle_post_valid
    codelist = create_codelist_from_scratch(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "…pythons/lib/python3.12/contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "…local/src/github.com/opensafely-core/opencodelists/codelists/actions.py", line 198, in create_codelist_from_scratch
    cache_hierarchy(version=version)
  File "…local/src/github.com/opensafely-core/opencodelists/codelists/actions.py", line 654, in cache_hierarchy
    hierarchy = version.calculate_hierarchy()
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "…local/src/github.com/opensafely-core/opencodelists/codelists/models.py", line 420, in calculate_hierarchy
    return self._calculate_new_style_hierarchy()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "…local/src/github.com/opensafely-core/opencodelists/codelists/models.py", line 432, in _calculate_new_style_hierarchy
    return Hierarchy.from_codes(self.coding_system, list(code_to_status))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "…local/src/github.com/opensafely-core/opencodelists/codelists/hierarchy.py", line 44, in from_codes
    ancestor_relationships = set(coding_system.ancestor_relationships(codes))
                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "…local/src/github.com/opensafely-core/opencodelists/coding_systems/bnf/coding_system.py", line 68, in ancestor_relationships
    return query(sql, codes, database=self.database_alias)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "…local/src/github.com/opensafely-core/opencodelists/opencodelists/db_utils.py", line 8, in query
    with connections[database].cursor() as c:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "…venv/opencodelists-3.12/lib/python3.12/site-packages/django/test/testcases.py", line 198, in __call__
    raise DatabaseOperationForbidden(self.message)
django.test.testcases.DatabaseOperationForbidden: Database queries to 'bnf_test_20200101' are not allowed in this test. Add 'bnf_test_20200101' to pytest_django.fixtures._django_db_helper.<locals>.PytestDjangoTestCase.databases to ensure proper test isolation and silence this failure.
FAILED opencodelists/tests/functional/test_build_codelist.py::test_build_bnf_codelist_single_search[non_organisation_login_context-asthma]
FAILED opencodelists/tests/functional/test_build_codelist.py::test_build_bnf_codelist_single_search[organisation_login_context-asthma]

I'm not quite sure why, because at least the BNF fixture being used is listed in the Playwright test (although this is about setting up the fixture, so maybe there's a issue of timing when this applies there?)

However, all the tests do run independently: if you assign the same number of workers as tests, so every test runs in a different worker, all the Playwright tests pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant