2

I've created an abstract model class that can be used to create concrete models. Since I intend to put this in a separate django module to be re-usable, I don't have a concrete model in my package. I'm testing my abstract class AbstractBaseModel by dynamically creating a concrete model in my tests:

class AbstractModelTestCase(TestCase):

    @classmethod
    def setUpClass(cls):

        class LocationTestModel(AbstractBaseModel):
            name = models.CharField(max_length=100)
            title = models.CharField(max_length=100, blank=True)
            num = models.IntegerField(default=0)

        try:
            with connection.schema_editor() as editor:
                editor.create_model(LocationTestModel)
            super(AbstractModelTestCase, cls).setUpClass()
        except ProgrammingError:
            pass

        cls.Location = LocationTestModel

    @classmethod
    def tearDownClass(cls):
        try:
            with connection.schema_editor() as editor:
                editor.delete_model(cls.Location)
            super(AbstractModelTestCase, cls).tearDownClass()
        except ProgrammingError:
            pass

This works fine: In my test cases, self.Location is my test model and I can create and manipulate it as any other concrete model.

Now I'd like to test a management command using call_command. My management command takes an app_label as argument and then looks for all the models in the app app_label, checking which models are sub-instances of my AbstractBaseModel, like this:

    def get_models_to_handle(self, app_label):
        try:
            app_to_handle = apps.get_app_config(app_label)
        except LookupError:
            app_to_handle = None

        models = []
        if app_to_handle:
            app_models = app_to_handle.get_models()
            models.extend(model for model in app_models if issubclass(model, AbstractBaseModel))

        return models

The goal is to do something for each of these models.

However, this won't work for my test case, since the AbstractModelTestCase.Location model isn't registered as part of an app, so there's no app_label I can pass.

How can I test my management command with my dynamically created test model? Is there a way to register it as part of a test app? Or is there a different way to define a test app and test model that are only there for testing purposes without resorting to using setUpClass?

I've seen this post, but I don't understand what is needed to add a test app to INSTALLED_APPS dynamically in recent versions of Django (1.11+) (the example link referred to is broken).

dirkgroten
  • 20,112
  • 2
  • 29
  • 42
  • Have you tried to add `managed = False` to `LocationTestModel` as a meta option? – Davit Tovmasyan Oct 14 '19 at 12:17
  • Why would that make any difference? As I said the test model works just fine. The table is created in the db and I can create and save instances. The `managed` option only relates to makemigrations. The problem is that the model doesn’t belong to any app. So my management command doesn’t find it when called in the test (it works fine when used in an app that has a concrete model subclass of my abstract class). – dirkgroten Oct 14 '19 at 12:51
  • Is it not an option to define the LocationTestModel inside a test app models.py file, and then create a specific settings file for running tests where you include your test app as defined in the [docs here](https://docs.djangoproject.com/en/2.2/topics/testing/advanced/#using-the-django-test-runner-to-test-reusable-applications)? – Nico Griffioen Oct 14 '19 at 14:13
  • 1
    @NicoGriffioen thanks, that's definitely an option. I've just looked at a few other packages that create abstract models and indeed, they just bundle a special test app with its models, migrations and settings to run the tests with. I guess I was still looking at it from the point of view my project (which uses the standalone package I made) and currently runs these tests as well, so thought changing the settings to use isn't an option. – dirkgroten Oct 14 '19 at 14:45

0 Answers0