-
-
Save GeeWee/54b6fd7ad87bcc876781ae02d1e0993d to your computer and use it in GitHub Desktop.
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 |
Hi,
Am trying to customize your gist to work in my setup but I keep running into what seems like a basic error but I can't figure out how to fix it.
Am able to create the public tenant. Issue is, at the point of creating an actual tenant
, am getting an error: INTERNALERROR> Failed: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.
I've tried adding @pytest.mark.django_db
to the django_db_setup
but that doesn't work.
My setup is like your but in my case instead of having tenant = Tenant(name=TENANT_NAME, schema_name=TENANT_NAME, domain_url='test-tenant.test.com')
I am creating the tenant via django-tenant-users which has a provision_tenant
function i.e. provision_tenant("EvilCorp", "evilcorp", "[email protected]")
. As such, I was not expecting that function it to be flagged for db_access given that your Create Tenant Function is not requiring the db flag?
I didn't see your comment. I've updated the gist to the current one I'm using, which is potentially more useful
Wonder why you omitted the import statements from the file? What's ServiceType
?
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.
Usage: