Pytest-django: `mail.outbox` não está sendo definido?

Criado em 10 abr. 2018  ·  14Comentários  ·  Fonte: pytest-dev/pytest-django

Olá, obrigado por pytest-django! Aprecie todo o trabalho árduo, usei-o algumas vezes sem problemas.

Estamos enfrentando um problema quando atualizamos algumas coisas e de repente mail.outbox não está retendo e-mails durante os testes. Tentei descobrir como usar exatamente o fixture mailoutbox conforme documentado aqui, mas estou tendo problemas para usar isso.

No momento, estamos testando em uma classe e tentei algumas configurações para aprovar mailoutbox fixture. Ou seja, eu atualizei o ini:

[pytest]
usefixtures = mailoutbox

Não tenho certeza de como usar aquele acessório, porém, tentei adicionar o argumento posicional mailoutbox aos meus métodos de classe, ainda não consegui acessá-lo .. Tenho certeza de que estou perdendo algo simples.

Agradeceria um empurrão na direção certa - e novamente, muito obrigado por este projeto!

bug

Comentários muito úteis

Acho que estou tendo o mesmo problema, então decidi reduzir meu teste para um envio de e-mail básico e executá-lo individualmente:

def test_reset_password(mailoutbox, db, settings):
    text_message = render_to_string('emails/password_reset/password_reset_successful.txt',
                                    context={})
    html_message = render_to_string('emails/password_reset/password_reset_successful.html',
                                    context={})
    subject = render_to_string('emails/password_reset/password_reset_successful_subject.txt',
                               context={})

    email = EmailMultiAlternatives(
        subject=subject,
        body=text_message,
        from_email="[email protected]",
        to=['[email protected]']
    )

    if html_message:
        email.attach_alternative(html_message, "text/html")
    email.send()

    print(settings.EMAIL_BACKEND)

    assert len(mailoutbox) == 1
>       assert len(mailoutbox) == 1
E       assert 0 == 1
E        +  where 0 = len([])

accounts/tests/api/test_reset_password.py:200: AssertionError
----------------------------------------------- Captured stdout call ------------------------------------------------
django.core.mail.backends.locmem.EmailBackend
--------------------------------------------- Captured stdout teardown ----------------------------------------------

Agora, a parte legal é que, se eu remover o acessório db , o teste passa. Tentei excluir meu método django_db_setup , mas sem sucesso ...

Estou preso no momento.

Todos 14 comentários

Consulte https://github.com/pytest-dev/pytest-django/blob/5da0935731d71aa347c57cd1753f51e3ba9f32d5/docs/helpers.rst#clearing -of-mailoutbox (7aee367).
(Não está claro se eu entendi o problema corretamente)

Acho que estou tendo o mesmo problema, então decidi reduzir meu teste para um envio de e-mail básico e executá-lo individualmente:

def test_reset_password(mailoutbox, db, settings):
    text_message = render_to_string('emails/password_reset/password_reset_successful.txt',
                                    context={})
    html_message = render_to_string('emails/password_reset/password_reset_successful.html',
                                    context={})
    subject = render_to_string('emails/password_reset/password_reset_successful_subject.txt',
                               context={})

    email = EmailMultiAlternatives(
        subject=subject,
        body=text_message,
        from_email="[email protected]",
        to=['[email protected]']
    )

    if html_message:
        email.attach_alternative(html_message, "text/html")
    email.send()

    print(settings.EMAIL_BACKEND)

    assert len(mailoutbox) == 1
>       assert len(mailoutbox) == 1
E       assert 0 == 1
E        +  where 0 = len([])

accounts/tests/api/test_reset_password.py:200: AssertionError
----------------------------------------------- Captured stdout call ------------------------------------------------
django.core.mail.backends.locmem.EmailBackend
--------------------------------------------- Captured stdout teardown ----------------------------------------------

Agora, a parte legal é que, se eu remover o acessório db , o teste passa. Tentei excluir meu método django_db_setup , mas sem sucesso ...

Estou preso no momento.

Esqueci de mencionar que existem alguns testes, no mesmo projeto, onde o fixture mailoutbox parece funcionar, então tentei detectar alguma diferença entre esses testes e o que falhou.
A única diferença que encontrei é o posicionamento do argumento mailoutbox na função de teste. Se for antes de qualquer acessório que dependa do acessório db , ele não funcionará, mas funcionará se eu colocá-lo depois, então acho que encontrei uma solução alternativa.

Posso confirmar que estamos vendo esse comportamento também com pytest-django == 3.3.3

@bogdanpetrea
Obrigado pela investigação / atualização.
Será útil para alguém que está depurando / corrigindo isso.

Não tenho certeza se está realmente relacionado, mas descobri que pode haver um AttributeError devido a mail.outbox não ter sido definido em primeiro lugar - mas isso está relacionado à mudança de ambiente enquanto o pytest-django gira.
Ref: https://github.com/pytest-dev/pytest-django/pull/708

Atualmente, tenho o problema, essa caixa de correio não está vazia :(
É o último argumento da função de teste.

EDIT: Eu chamo mailoutbox.clear() antes do código de teste real como work-a-round.

@jedie
Você pode fornecer um caso de teste reproduzível? Um teste de falha para pytest-django seria o melhor, é claro.

Relacionado a # 708? (ou seja, vasculhe o código que ele toca lá)

Eu criei um caso de teste trivial que reproduz o problema (pelo menos do jeito que estou passando): https://github.com/pytest-dev/pytest-django/compare/master...koniiiik : 589- mailoutbox-is-not-django-core-mail-outbox? expand = 1

Da forma como usei mailoutbox , também seria muito útil poder fazer mailoutbox.clear() realmente limpar mailoutbox e mail.outbox . Se mailoutbox is mail.outbox , então alguém poderia limpar a caixa de saída antes de executar o código que deveria gerar um número específico de e-mails (por exemplo, 1 e-mail).

Não tenho certeza se isso está relacionado a este problema, mas o fixture mailoutbox não corresponde ao comportamento de mail.outbox em meu teste. Não está coletando e-mails enviados:

(Pdb) mail.outbox
[<django.core.mail.message.EmailMultiAlternatives object at 0x7f6d864cb198>]
(Pdb) mailoutbox
[]
(Pdb)

Não tenho certeza se isso está relacionado a este problema, mas o fixture mailoutbox não corresponde ao comportamento de mail.outbox em meu teste. Não está coletando e-mails enviados:

(Pdb) mail.outbox
[<django.core.mail.message.EmailMultiAlternatives object at 0x7f6d864cb198>]
(Pdb) mailoutbox
[]
(Pdb)

Mesmo.
mail.outbox contém uma mensagem enquanto mailoutbox está vazio.
A versão do pytest-django é 3.9.0.

Eu tenho o mesmo problema.

Se eu usar o acessório assim, ele falhará:

def test_send_foo_mail(mailoutbox, user_client, foo):

Assim funciona:

def test_send_foo_mail(user_client, foo, mailoutbox):

Levei muito tempo para descobri-lo. Eu gostaria de ajudar a depurar isso. Eu olhei para a implementação, mas não tenho ideia de como depurar isso.

Relacionado: https://stackoverflow.com/questions/66846621/mailoutbox-works-only-if-last-fixture

Experimentando o mesmo comportamento do comentarista anterior. Usar assim não funciona:

def test_command_and_email(
    mailoutbox, argument, time_machine, mocker 
):

Mas tudo funciona como esperado se eu apenas colocá-lo no último lugar.

def test_command_and_email(
    argument, time_machine, mocker, mailoutbox
):

O argumento argument também é um fixture que usa o fixture db , então acho que essa é a principal razão deste comportamento.
Estou usando pytets-django 4.3.0

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

Questões relacionadas

clintonb picture clintonb  ·  4Comentários

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

WoLpH picture WoLpH  ·  7Comentários

rodrigorodriguescosta picture rodrigorodriguescosta  ·  4Comentários

jedie picture jedie  ·  7Comentários