Pytest setup class once before testing

1.7k views Asked by At

I'm using pytest for testing with mixer library for generating model data. So, now I'm trying to setup my tests once before they run. I grouped them into TestClasses, set to my fixtures 'class' scope, but this doesn't work for me.

@pytest.mark.django_db
class TestCreateTagModel:

@classmethod
@pytest.fixture(autouse=True, scope='class')
def _set_up(cls, create_model_instance, tag_model, create_fake_instance):
    cls.model = tag_model
    cls.tag = create_model_instance(cls.model)
    cls.fake_instance = create_fake_instance(cls.model)
    print('setup')

def test_create_tag(self, tag_model, create_model_instance, check_instance_exist):
    tag = create_model_instance(tag_model)
    assert check_instance_exist(tag_model, tag.id)

conftest.py

pytest.fixture(scope='class')
@pytest.mark.django_db(transaction=True)
def create_model_instance():
    instance = None
    def wrapper(model, **fields):
        nonlocal instance
    
        if not fields:
            instance = mixer.blend(model)
        else:
            instance = mixer.blend(model, **fields)  
        return instance

    yield wrapper
    if instance:
        instance.delete()




@pytest.fixture(scope='class')
@pytest.mark.django_db(transaction=True)
def create_fake_instance(create_related_fields):
"""
    Function for creating fake instance of model(fake means that this instance doesn't exists in DB)
    
    Args:
        related (bool, optional): Flag which indicates create related objects or not. Defaults to False.
"""
    instance = None
    def wrapper(model, related=False, **fields):
        with mixer.ctx(commit=False):
            instance = mixer.blend(model, **fields)
         
            if related:
                    create_related_fields(instance, **fields)
            return instance
    
    yield wrapper
    if instance:
        instance.delete()
       

@pytest.fixture(scope='class')
@pytest.mark.django_db(transaction=True)
def create_related_fields():
    django_rel_types = ['ForeignKey']

    def wrapper(instance, **fields):
        for f in instance._meta.get_fields():
            if type(f).__name__ in django_rel_types:
                rel_instance = mixer.blend(f.related_model)

                setattr(instance, f.name, rel_instance)

    return wrapper

But I'm catching exception in mixer gen_value method: Database access not allowed, use django_db mark(that I'm already use). Do you have any ideas how this can be implemented?

1

There are 1 answers

0
Bryan Gillis On BEST ANSWER

You can set things up once before a run by returning the results of the setup, rather than modifying the testing class directly. From my own attempts, it seems any changes to the class made within class-scope fixtures are lost when individual tests are run. So here's how you should be able to do this. Replace your _setup fixture with these:

@pytest.fixture(scope='class')
def model_instance(self, tag_model, create_model_instance):
    return create_model_instance(tag_model)

@pytest.fixture(scope='class')
def fake_instance(self, tag_model, create_fake_instance):
    return create_fake_instance(tag_model)

And then these can be accessed through:

def test_something(self, model_instance, fake_instance):
    # Check that model_instance and fake_instance are as expected

I'm not familiar with Django myself though, so there might be something else with it going on. This should at least help you solve one half of the problem, if not the other.