web-dev-qa-db-fra.com

Comment affirmer correctement qu'une exception est levée dans pytest?

Code:

# coding=utf-8
import pytest


def whatever():
    return 9/0

def test_whatever():
    try:
        whatever()
    except ZeroDivisionError as exc:
        pytest.fail(exc, pytrace=True)

Sortie:

================================ test session starts =================================
platform linux2 -- Python 2.7.3 -- py-1.4.20 -- pytest-2.5.2
plugins: Django, cov
collected 1 items 

pytest_test.py F

====================================== FAILURES ======================================
___________________________________ test_whatever ____________________________________

    def test_whatever():
        try:
            whatever()
        except ZeroDivisionError as exc:
>           pytest.fail(exc, pytrace=True)
E           Failed: integer division or modulo by zero

pytest_test.py:12: Failed
============================== 1 failed in 1.16 seconds ==============================

Comment faire une trace d'impression pytest pour que je puisse voir où dans la fonction whatever une exception a été déclenchée?

165
Gill Bates

pytest.raises(Exception) est ce dont vous avez besoin.

Code

import pytest

def test_passes():
    with pytest.raises(Exception) as e_info:
        x = 1 / 0

def test_passes_without_info():
    with pytest.raises(Exception):
        x = 1 / 0

def test_fails():
    with pytest.raises(Exception) as e_info:
        x = 1 / 1

def test_fails_without_info():
    with pytest.raises(Exception):
        x = 1 / 1

# Don't do this. Assertions are caught as exceptions.
def test_passes_but_should_not():
    try:
        x = 1 / 1
        assert False
    except Exception:
        assert True

# Even if the appropriate exception is caught, it is bad style,
# because the test result is less informative
# than it would be with pytest.raises(e)
# (it just says pass or fail.)

def test_passes_but_bad_style():
    try:
        x = 1 / 0
        assert False
    except ZeroDivisionError:
        assert True

def test_fails_but_bad_style():
    try:
        x = 1 / 1
        assert False
    except ZeroDivisionError:
        assert True

Sortie

============================================================================================= test session starts ==============================================================================================
platform linux2 -- Python 2.7.6 -- py-1.4.26 -- pytest-2.6.4
collected 7 items 

test.py ..FF..F

=================================================================================================== FAILURES ===================================================================================================
__________________________________________________________________________________________________ test_fails __________________________________________________________________________________________________

    def test_fails():
        with pytest.raises(Exception) as e_info:
>           x = 1 / 1
E           Failed: DID NOT RAISE

test.py:13: Failed
___________________________________________________________________________________________ test_fails_without_info ____________________________________________________________________________________________

    def test_fails_without_info():
        with pytest.raises(Exception):
>           x = 1 / 1
E           Failed: DID NOT RAISE

test.py:17: Failed
___________________________________________________________________________________________ test_fails_but_bad_style ___________________________________________________________________________________________

    def test_fails_but_bad_style():
        try:
            x = 1 / 1
>           assert False
E           assert False

test.py:43: AssertionError
====================================================================================== 3 failed, 4 passed in 0.02 seconds ======================================================================================

Notez que e_info enregistre l'objet exception afin que vous puissiez en extraire les détails. Par exemple, si vous souhaitez vérifier la pile d'appels d'exception ou une autre exception imbriquée à l'intérieur. 

193
Murilo Giacometti

Voulez-vous dire quelque chose comme ça:

def test_raises():
    with pytest.raises(Exception) as excinfo:   
        raise Exception('some info')   
    assert excinfo.value.message == 'some info' 
88
simpleranchero

Il existe deux façons de traiter ce type de cas dans pytest:

  • Utiliser la fonction pytest.raises 

  • Utiliser pytest.mark.xfail decorator

Utilisation de pytest.raises:

def whatever():
    return 9/0
def test_whatever():
    with pytest.raises(ZeroDivisionError):
        whatever()

Utilisation de pytest.mark.xfail:

@pytest.mark.xfail(raises=ZeroDivisionError)
def test_whatever():
    whatever()

Sortie de pytest.raises:

============================= test session starts ============================
platform linux2 -- Python 2.7.10, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 -- 
/usr/local/python_2.7_10/bin/python
cachedir: .cache
rootdir: /home/user, inifile:
collected 1 item

test_fun.py::test_whatever PASSED


======================== 1 passed in 0.01 seconds =============================

Sortie du marqueur pytest.xfail:

============================= test session starts ============================
platform linux2 -- Python 2.7.10, pytest-3.2.3, py-1.4.34, pluggy-0.4.0 -- 
/usr/local/python_2.7_10/bin/python
cachedir: .cache
rootdir: /home/user, inifile:
collected 1 item

test_fun.py::test_whatever xfail

======================== 1 xfailed in 0.03 seconds=============================

Comme le dit la documentation :

Utiliser pytest.raises est probablement préférable dans les cas où vous testez des exceptions que votre propre code soulève délibérément, alors qu'utiliser @pytest.mark.xfail avec une fonction de vérification est probablement préférable pour quelque chose comme la documentation de bogues non corrigés (où le test décrit ce qui devrait se passer) dans les dépendances.

33
veri_pudcha_coder

tu peux essayer

def test_exception():
    with pytest.raises(Exception) as excinfo:   
        function_that_raises_exception()   
    assert str(excinfo.value) == 'some info' 
30
d_j

pytest évolue constamment et avec l'un des changements de Nice dans le passé récent, il est maintenant possible de simultanément tester

  • le type d'exception (test strict)
  • le message d'erreur (contrôle strict ou lâche utilisant une expression régulière)

Deux exemples de la documentation:

with pytest.raises(ValueError, match='must be 0 or None'):
    raise ValueError('value must be 0 or None')
with pytest.raises(ValueError, match=r'must be \d+$'):
    raise ValueError('value must be 42')

J'ai utilisé cette approche dans un certain nombre de projets et je l'aime beaucoup.

4
Jan-Philip Gehrcke

Cette solution est ce que nous utilisons: 

def test_date_invalidformat():
    """
    Test if input incorrect data will raises ValueError exception
    """
    date = "06/21/2018 00:00:00"
    with pytest.raises(ValueError):
        app.func(date) #my function to be tested

Veuillez vous référer à pytest, http://pytest.readthedocs.io/en/reorganize-docs/assert.html#assert

3
SMDC

La bonne façon est d'utiliser pytest.raises mais j'ai trouvé une alternative intéressante dans les commentaires ici et je veux la sauvegarder pour les futurs lecteurs de cette question:

try:
    thing_that_rasises_typeerror()
    assert False
except TypeError:
    assert True
3
Alexey Shrub

Avez-vous essayé de supprimer "pytrace = True"?

pytest.fail(exc, pytrace=True) # before
pytest.fail(exc) # after

Avez-vous essayé de courir avec '--fulltrace'?

0
Loic Pantou

Une meilleure pratique consiste à utiliser une classe qui hérite de unittest.TestCase et à exécuter self.assertRaises.

Par exemple:

import unittest


def whatever():
    return 9/0


class TestWhatEver(unittest.TestCase):

    def test_whatever():
        with self.assertRaises(ZeroDivisionError):
            whatever()

Ensuite, vous l'exécuteriez en exécutant:

pytest -vs test_path
0
kerbelp