Last active
February 1, 2023 20:26
-
-
Save GeeWee/54b6fd7ad87bcc876781ae02d1e0993d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
TEST_TENANT_NAME = "test" | |
@pytest.fixture(scope="function") | |
def db(request, django_db_setup, django_db_blocker): | |
from django.test import TestCase, TransactionTestCase | |
""" | |
Django db fixture. | |
Seeing as this has the same signature, it overrides the fixture in pytest_django.fixtures | |
It doesn't support quite the same things, such as transactional tests or resetting of sequences. | |
The way our setup works here, is that due to performance reasons we want to override this, and then | |
create our public and test tenant, before entering into the `atomic` block that pytest and Django normally | |
runs tests in - this way we only have to do the heavy work of migrating our schemas once every test run, rather | |
than every test. | |
""" | |
logger.info("Fetching the DB fixture") | |
if is_django_unittest(request): | |
return | |
# Some weird django db_blocker magic | |
django_db_blocker.unblock() | |
request.addfinalizer(django_db_blocker.restore) | |
# Create the public and the test tenant. | |
# We do this right before the pre_setup so it doesn't | |
# get axed by the atomic block() | |
Tenant.objects.get_or_create(schema_name=get_public_schema_name()) | |
_get_or_create_test_tenant() | |
""" Here we distinguish between a transactional test or not (corresponding to Djangos | |
TestCase or its TransactionTestCase. Some tests that create/drop tenants can't be run | |
inside an atomic block, so must be marked as transactional""" | |
if "transactional" in request.keywords: | |
test_case = TransactionTestCase(methodName="__init__") | |
logger.debug("Using transactional test case") | |
else: | |
# This performs the rest of the test in an atomic() block which will roll back the changes. | |
logger.debug("Using regular test case") | |
test_case = TestCase(methodName="__init__") | |
test_case._pre_setup() | |
# Post-teardown function here reverts the atomic blocks to leave the DB in a fresh state. | |
request.addfinalizer(test_case._post_teardown) # This rolls the atomic block back | |
@pytest.fixture(scope="function") | |
def user(run_in_tenant_context) -> User: | |
from django.contrib.auth import get_user_model | |
from model_mommy import mommy | |
return mommy.make(get_user_model(), tenant=_get_or_create_test_tenant()) | |
@pytest.fixture(scope="function") | |
def api_client(user) -> APIClient: | |
""" Returns a logged in APIClient from DRF. Creates a user-model as a side-effect.""" | |
logger.info("Fetching api_client fixture") | |
client = APIClient() | |
client.force_login(user) | |
# Run test | |
return client | |
@pytest.fixture(scope="function", autouse=True) | |
def run_in_tenant_context(db, request, caplog): | |
# Here we create the statics | |
logger.info("Running in test tenant context") | |
with tenant_context(_get_or_create_test_tenant()): | |
# Delete unknown offloading ServiceType from datamigration | |
ServiceType.objects.all().delete() | |
# Create statics if needed | |
if "no_statics" not in request.keywords: | |
# We don't want to show setup logs normally | |
with caplog.at_level(logging.WARNING): | |
logger.info("Populating static tables") | |
populate_static_database_tables() | |
yield | |
def _get_or_create_test_tenant() -> Tenant: | |
""" | |
Fixture that gives us a test_tenant if we need it | |
""" | |
try: | |
tenant = Tenant.objects.get(schema_name=TEST_TENANT_NAME) | |
logger.debug("Using previously created tenant") | |
return tenant | |
except Tenant.DoesNotExist: | |
logger.debug("Creating new test tenant") | |
tenant = Tenant(schema_name=TEST_TENANT_NAME) | |
tenant.save(verbosity=0) # This saves the tenant and creates the tenant schema. | |
return tenant |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can't really recall why I omitted the import statements anymore.
ServiceType
is just a application-specific thing for my app. You can omit that line and it should work just fine.