3

General issue: I have an abstract model that I want to test with a real model instance, however I don't want to have to completely restructure my project/test format. See these two answers for reference: First answer Second answer

I want to A) Define models inside each test app folder and not define them inside the actual apps B) Not have an additional/separate apps.py and settings.py configuration for each test folder. I know this is possible because the django project has a very similar test structure like this, but they use a test run script that I can't entirely decipher.

The test/ folder mirrors the app structure I have (which are blog/, projects/, richeditable/).

backend
├── projects
├── blog
├── richeditable
├── tests
│   ├── __init__.py
│   ├── conftest.py
│   ├── blog
│   │   ├── ...
│   ├── projects
│   │   ├── ...
│   └── richeditable
│   │   ├── __init__.py
│   │   ├── test_model.py
│   │   ├── models.py         <-------------- This defines a model that inherits from richeditable.models
# richeditable/models.py
class Draftable(models.Model):
    blah = models.IntegerField()

    class Meta:
        abstract = True
# tests/richeditable/models.py
class DummyDraftable(Draftable):
    additional_field = models.BooleanField()

#   class Meta:
#       app_label = "some_app" 
#  ^--- Django does not seem to like having this blank and I don't know what to put to make this work
# tests/richeditable/test_model.py
import pytest

@pytest.fixture
def add_app(settings):
    settings.INSTALLED_APPS += ['backend.tests.richeditable']
    
# presumably the above fixture would affect the apps/models
# settings before the database is created, but that does not seems to be the case
def test_please_work(add_app, db):
    assert DummyDraftable.objects.create(blah=1, additional_field=True) is not None

My best guess from what I've seen of the django project script is that they load in each folder for testing as a module and add it to INSTALLED_APPS at run time before the test cases. However, you can't just simply change INSTALLED_APPS because models are being added and migrations have to be made to the test database beforehand as well as there seems to be a need to define an AppConfig (because Django loses it's mind if you don't). I've tried to include the app_label Meta field for models but it didn't work, or I may have done something wrong. But the point is, I don't see the script creating an AppConfig and they somehow don't have to declare Meta in their models.py


Pytest specific stuff: Things get further complicated with pytest-django because it doesn't use Django's TestRunner interface. This is how you would do it if that were the case (note the order of operations). I have already tried modifying the settings pytest fixture before instantiating the db with the associated fixtures but this doesn't end up loading the module no matter what I do. From looking at the source code, it seems like the settings are fixed in place based on what the settings.py specifies and modifying the settings fixture makes no difference to app loading.


Kalin Kochnev
  • 317
  • 4
  • 12
  • 1
    Modifying `INSTALLED_APPS` after `django.setup()` was called has no effect anymore, as Django won't reload them. Create custom settings module for testing that inherits from the production one and modify `INSTALLED_APPS` there. You can then pass custom settings via `pytest-django`'s options. – hoefling Jan 09 '22 at 12:41

1 Answers1

3

So I ended up solving my own issue. As @hoefling mentioned, the best way is to create a separate settings file that extends your normal settings file and specify that as your pytest settings file.

// pytest.ini
[pytest]
addopts = --ds=config.settings.test

An important thing to note is that you cannot have your test modules be the same name as already existing apps, as I found out. So the structure I had with backend/tests/richeditable is not allowed no matter what you do. So I prepended each folder with test_ and it works fine. This also solves the issue of having to include app_label in your models.

# testsettings.py
from .settings import *  # noqa

INSTALLED_APPS += [
    'backend.tests.test_projects',
    'backend.tests.test_blog',
    'backend.tests.test_richeditable',
]
backend
├── projects
├── blog
├── richeditable
├── tests
│   ├── __init__.py
│   ├── conftest.py
│   ├── test_blog
│   │   ├── ...
│   ├── test_projects
│   │   ├── ...
│   └── test_richeditable
│   │   ├── __init__.py
│   │   ├── test_model.py
│   │   ├── models.py  
Kalin Kochnev
  • 317
  • 4
  • 12