Pytest-django: Como usar a marca django_db no dispositivo de sessão?

Criado em 14 set. 2017  ·  15Comentários  ·  Fonte: pytest-dev/pytest-django

Eu tenho uma situação em que preciso carregar um acessório de banco de dados enorme que é criado por uma função. O acessório é necessário em todos os testes de API. Então eu fiz uma sessão de fixação em conftest.py que faria isso. Mas o problema é que pytest lança a seguinte exceção, mesmo que eu tenha marcado django_db :

E Failed: Database access not allowed, use the "django_db" mark to enable it.
Abaixo está o meu trecho de código.

from permission.helpers import update_permissions

pytestmark = [
        pytest.mark.django_db(transaction = True),]

@pytest.fixture(scope="session", autouse = True)
def permission(request):
        load_time_consuming_db_fixture()
db-configuration enhancement

Comentários muito úteis

Este tem sido o maior ponto problemático para mim vindo dos testes baseados em classe UnitTest do Django, para pytest-django -- no Django usamos setUpTestData para executar operações de banco de dados caras uma vez (equivalente a fixtures pytest com escopo de sessão), e então há um truque astuto para executar obj.refresh_from_db() no setUp para atualizar as referências de classe.

Mesmo se eu estiver apenas criando uma instância de modelo de banco de dados e recarregando-a a cada TC, isso é quase sempre mais rápido do que criar em cada caso de teste.

Seria ótimo se pudéssemos obter os fixtures com escopo de sessão de pytest-tipsi-django mesclados upstream, se isso for uma possibilidade; Demorou um pouco de escavação para encontrar este problema e solução.

Todos 15 comentários

@ludbek também perdemos esse recurso e criamos um plugin para este recurso:
https://github.com/tipsi/pytest-tipsi-django (uso: https://github.com/tipsi/pytest-tipsi-django/blob/master/test_django_plugin/app/tests/test_transactions.py)
Em conjunção com:
https://github.com/tipsi/pytest-tipsi-testing
Ele oferece a capacidade de aninhar transações e corrigir a ordem de execução/encerramento.

@cybergrind Obrigado por responder. Com certeza vou conferir e te conto como foi.

Este tem sido o maior ponto problemático para mim vindo dos testes baseados em classe UnitTest do Django, para pytest-django -- no Django usamos setUpTestData para executar operações de banco de dados caras uma vez (equivalente a fixtures pytest com escopo de sessão), e então há um truque astuto para executar obj.refresh_from_db() no setUp para atualizar as referências de classe.

Mesmo se eu estiver apenas criando uma instância de modelo de banco de dados e recarregando-a a cada TC, isso é quase sempre mais rápido do que criar em cada caso de teste.

Seria ótimo se pudéssemos obter os fixtures com escopo de sessão de pytest-tipsi-django mesclados upstream, se isso for uma possibilidade; Demorou um pouco de escavação para encontrar este problema e solução.

oi @paultiplady

Não tenho certeza se essa abordagem de pytest-tipsi-djanjo se encaixa no modelo de teste usual para pytest . A diferença mais notável é o acabamento dos equipamentos: atualmente pytest não finaliza explicitamente os equipamentos desnecessários com um escopo mais amplo. Portanto, você precisa terminar as transações explicitamente em uma ordem específica e, em geral, isso pode causar efeitos muito diferentes (atualmente pytest pode manter os fixtures ativos mesmo se o teste ativo e seus fixtures não exigirem nada).

Tivemos que alterar os testes em nosso projeto para essa abordagem porque às vezes precisamos testar alguns grandes cenários e substituímos o gerenciamento manual de transações existente em testes enormes por acessórios um pouco melhores, mas ainda requer atenção nos testes de pedidos.

No momento, vejo apenas uma solução para isso: colocar algum tipo de FAQ na documentação.

Obrigado pelo detalhe adicional @cybergrind. Eu me aprofundei um pouco mais, mas não tenho tempo para hoje -- aqui é onde eu tenho que fazer, eu apreciaria uma verificação de sanidade sobre se essa abordagem é útil ou não, já que estou não tão familiarizado com os internos do pytest.

Também não entendo o que você quer dizer com "pytest não termina explicitamente acessórios desnecessários com um escopo mais amplo", você poderia expandir um pouco mais, por favor? Isso se refere aos finalizadores? Isso pode afetar o que escrevi abaixo.

O plugin pytest-django usa a marca django_db , que é tratada em _django_db_marker em plugin.py (https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/plugin.py #L375), chamando o fixture db com escopo de função (https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/fixtures.py#L142). Este acessório instancia um Django TestCase e chama sua função _pre_setup (e enfileira o _post_teardown ).

Posso ver algumas opções:

Gostaria de saber se poderíamos estender _django_db_marker para opcionalmente fazer aulas ou sessões
configuração com escopo também, que faria essencialmente a mesma coisa que db , mas chamando o equivalente a cls.setUpTestData ou uma função passada na marca kwargs.

Para escopo de classe, e estou assumindo também para escopo de sessão, o comportamento esperado seria reverter o banco de dados posteriormente, então basicamente precisamos de acessórios com escopo que acionem na ordem correta e que cada um configure seu próprio atomic transação. Eu acredito que isso significa que isso precisa ser uma modificação no fixture db , ao invés de um fixture separado que corre ao lado dele.

Dessa forma, acionaríamos corretamente qualquer configuração de nível de classe/sessão especificada, e essa configuração seria chamada uma vez por classe/sessão. Acredito que uma modificação na própria marca seja necessária porque se você acabou de configurar uma função com escopo de sessão que aciona manualmente django_db_blocker.unblock() , isso parece acontecer depois que a marca django_db configurou a primeira transação.

Isso pode ser algo assim (em plugin.py _django_db_marker() ):

    if marker:
        if marker.session_db:
            getfixturevalue(request, 'db_session')

        if marker.class_db:
            getfixturevalue(request, 'db_class')

        validate_django_db(marker)
        if marker.transaction:
            getfixturevalue(request, 'transactional_db')
        else:
            getfixturevalue(request, 'db')

Essa conversa é maluca ou vale a pena explorar mais esse tópico?

Sobre a finalização: https://github.com/tipsi/pytest-tipsi-testing/blob/master/tests/test_finalization.py

Este teste não funciona sem finalização explícita, da mesma forma que acessórios de banco de dados não funcionais. E isso é sobre a implementação do pytest, então não há nada a fazer no pytest-django para corrigi-lo.

Duplicata de #388 e #33

Obrigado, fechando.

33 está fechado e o nº 388 não contém nenhuma discussão significativa (ao contrário desta). Parece estranho fechar este @blueyed , se alguma coisa eu sugeriria fechar # 388 e tornar este o bilhete canônico para este problema.

👍 obrigado!

Eu também apreciaria muito essa funcionalidade.

@mkokotovich
Quantos? O suficiente para fazer isso acontecer sozinho? ;)

De qualquer forma, o problema principal / fundamental aqui (do comentário original) já é que o banco de dados é redefinido durante os testes, portanto, não há uma maneira trivial de ter um acessório com escopo de sessão como esse.

O que pode funcionar é algo junto:

@pytest.fixture(scope="session")
def django_db_setup(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        with transaction.atomic():  # XXX: could/should use `TestCase_enter_atomics` for multiple dbs
            load_time_consuming_db_fixture()
            yield

A ideia é envolver tudo em um bloco atômico adicional. Isso não foi testado no entanto, e você pode precisar usar TransactionTestCase na verdade para isso.

@paultiplady
Seu comentário parece bom - ou seja, vale a pena explorar mais AFAICS (veja também meu comentário anterior).

@blueyed estamos usando essa abordagem por mais de um ano (envolva tudo em um bloco atômico adicional) funciona muito bem.
Mas você não pode simplesmente incluir algo assim porque o pytest não possui o determenístico onde o nível de sessão (e outro superior ao teste) será fechado, portanto, exigirá o rastreamento de dependências de transações de banco de dados, independentemente de exigirem um ao outro ou não diretamente .
Portanto, você precisa rastrear explicitamente a pilha de transações e fechar as transações aninhadas antes do próximo teste. In pode ser feito da seguinte forma: https://github.com/tipsi/pytest-tipsi-django/blob/master/pytest_tipsi_django/django_fixtures.py#L46

desculpe, quero esclarecer, pois acredito que estou enfrentando o mesmo problema:

Se eu quiser criar um fixture que dependa da criação de um novo objeto db, pensei que poderia fazer

@pytest.mark.django_db(transaction=True)
@pytest.fixture(scope="session")
def object():
    object = Object.create_object(params)
    yield object
    // or alternatively
    object = mixer.blend('myObject')
    yield object

No entanto, recebo o seguinte erro na execução do caso de teste: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

koxu1996 picture koxu1996  ·  3Comentários

dan-passaro picture dan-passaro  ·  4Comentários

WoLpH picture WoLpH  ·  7Comentários

tolomea picture tolomea  ·  4Comentários

tolomea picture tolomea  ·  6Comentários