Pergunta

Como se escrever uma unittest que não somente se uma função não lançar uma exceção esperado?

Foi útil?

Solução

Use TestCase.assertRaises (ou TestCase.failUnlessRaises) do módulo unittest , por exemplo:

import mymod

class MyTestCase(unittest.TestCase):
    def test1(self):
        self.assertRaises(SomeCoolException, mymod.myfunc)

Outras dicas

Desde Python 2.7, você pode usar o gerente contexto costuma ficar o objeto exceção real lançada:

import unittest

def broken_function():
    raise Exception('This is broken')

class MyTestCase(unittest.TestCase):
    def test(self):
        with self.assertRaises(Exception) as context:
            broken_function()

        self.assertTrue('This is broken' in context.exception)

if __name__ == '__main__':
    unittest.main()

http://docs.python.org/dev/ biblioteca / unittest.html # unittest.TestCase.assertRaises


Em Python 3.5 , você tem que embrulhar context.exception em str, caso contrário, você vai ter um TypeError

self.assertTrue('This is broken' in str(context.exception))

O código na minha resposta anterior pode ser simplificada para:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction)

E se AFunction leva argumentos, apenas passá-los em assertRaises como este:

def test_afunction_throws_exception(self):
    self.assertRaises(ExpectedException, afunction, arg1, arg2)

Como você testar que uma função Python lança uma exceção?

Como se escreve um teste que falhar somente se uma função não joga uma exceção esperado?

Resposta curta:

Use o método self.assertRaises como um gerente de contexto:

    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'

Demonstração

A melhor abordagem prática é bastante fácil de demonstrar em um shell Python.

A biblioteca unittest

Em Python 2,7 ou 3:

import unittest

Em Python 2.6, você pode instalar um backport da biblioteca unittest 2.7 de, chamado unittest2 , e apenas alias que como unittest:

import unittest2 as unittest

Exemplo testa

Agora, cole em seu Python desembolsar o seguinte teste do tipo-segurança do Python:

class MyTestCase(unittest.TestCase):
    def test_1_cannot_add_int_and_str(self):
        with self.assertRaises(TypeError):
            1 + '1'
    def test_2_cannot_add_int_and_str(self):
        import operator
        self.assertRaises(TypeError, operator.add, 1, '1')

Test uma utilidades assertRaises como um gerente de contexto, o que garante que o erro está devidamente capturados e limpas, enquanto gravado.

Nós também poderia escrever sem o gerente contexto, ver teste de dois. O primeiro argumento seria o tipo de erro que você espera aumento, o segundo argumento, a função que você está testando, e os argumentos restantes e argumentos de palavra-chave serão passados ??para essa função.

Eu acho que é muito mais simples, legível e de fácil manutenção para apenas usar o gerenciador de contexto.

Executando os testes

Para executar os testes:

unittest.main(exit=False)

Em Python 2.6, você provavelmente vai necessário o seguinte :

unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

E o seu terminal deve saída o seguinte:

..
----------------------------------------------------------------------
Ran 2 tests in 0.007s

OK
<unittest2.runner.TextTestResult run=2 errors=0 failures=0>

E vemos que, como esperamos, tentando adicionar um 1 e um resultado '1' em um TypeError.


Para obter mais detalhado saída, tente o seguinte:

unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(MyTestCase))

O código deve seguir esse padrão (este é um unittest teste estilo módulo):

def test_afunction_throws_exception(self):
    try:
        afunction()
    except ExpectedException:
        pass
    except Exception as e:
       self.fail('Unexpected exception raised:', e)
    else:
       self.fail('ExpectedException not raised')

Em Python <2.7 esta construção é útil para verificar se há valores específicos na exceção prevista. O unittest assertRaises função somente verifica se uma exceção foi levantada.

De: http://www.lengrand.fr/2011 / 12 / pythonunittest-assertraises-raises de erros /

Em primeiro lugar, aqui é o correspondente (ainda dum: p), em função dum_function.py arquivo:

def square_value(a):
   """
   Returns the square value of a.
   """
   try:
       out = a*a
   except TypeError:
       raise TypeError("Input should be a string:")

   return out

Aqui está o teste a ser realizado (é inserida apenas este teste):

import dum_function as df # import function module
import unittest
class Test(unittest.TestCase):
   """
      The class inherits from unittest
      """
   def setUp(self):
       """
       This method is called before each test
       """
       self.false_int = "A"

   def tearDown(self):
       """
       This method is called after each test
       """
       pass
      #---
         ## TESTS
   def test_square_value(self):
       # assertRaises(excClass, callableObj) prototype
       self.assertRaises(TypeError, df.square_value(self.false_int))

   if __name__ == "__main__":
       unittest.main()

Agora estamos prontos para testar a nossa função! Aqui é o que acontece quando se tenta executar o teste:

======================================================================
ERROR: test_square_value (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_dum_function.py", line 22, in test_square_value
    self.assertRaises(TypeError, df.square_value(self.false_int))
  File "/home/jlengrand/Desktop/function.py", line 8, in square_value
    raise TypeError("Input should be a string:")
TypeError: Input should be a string:

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

A TypeError é actullay levantada, e gera uma falha no teste. O problema é que este é exatamente o comportamento que queríamos:. S

Para evitar este erro, basta executar a função usando lambda na chamada de teste:

self.assertRaises(TypeError, lambda: df.square_value(self.false_int))

A saída final:

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Perfect!

... e para mim é perfeito também !!

Thansk um monte Sr. Julien Lengrand-Lambert

Você pode construir seu próprio contextmanager para verificar se a exceção foi levantada.

import contextlib

@contextlib.contextmanager
def raises(exception):
    try:
        yield 
    except exception as e:
        assert True
    else:
        assert False

E, em seguida, você pode usar raises assim:

with raises(Exception):
    print "Hola"  # Calls assert False

with raises(Exception):
    raise Exception  # Calls assert True

Se você estiver usando pytest, essa coisa já está implementado. Você pode fazer pytest.raises(Exception):

Exemplo:

def test_div_zero():
    with pytest.raises(ZeroDivisionError):
        1/0

E o resultado:

pigueiras@pigueiras$ py.test
================= test session starts =================
platform linux2 -- Python 2.6.6 -- py-1.4.20 -- pytest-2.5.2 -- /usr/bin/python
collected 1 items 

tests/test_div_zero.py:6: test_div_zero PASSED

Eu uso doctest [1] quase todos os lugares, porque eu gosto do fato de que eu documento e testar minhas funções ao mesmo tempo.

Tenha um olhar para este código:

def throw_up(something, gowrong=False):
    """
    >>> throw_up('Fish n Chips')
    Traceback (most recent call last):
    ...
    Exception: Fish n Chips

    >>> throw_up('Fish n Chips', gowrong=True)
    'I feel fine!'
    """
    if gowrong:
        return "I feel fine!"
    raise Exception(something)

if __name__ == '__main__':
    import doctest
    doctest.testmod()

Se você colocar este exemplo em um módulo e executá-lo a partir da linha de comando ambos os casos de teste são avaliados e controlados.

[1] Python documentação: 23.2 doctest - exemplos de teste interativo Python

Tenha um olhar para o método assertRaises de o módulo unittest.

Eu só descobri que a Mock biblioteca fornece um método assertRaisesWithMessage () ( em sua subclasse unittest.TestCase), que irá verificar não só que a exceção prevista é levantada, mas também que é levantada com a mensagem esperado:

from testcase import TestCase

import mymod

class MyTestCase(TestCase):
    def test1(self):
        self.assertRaisesWithMessage(SomeCoolException,
                                     'expected message',
                                     mymod.myfunc)

Você pode usar assertRaises do módulo unittest

import unittest

class TestClass():
  def raises_exception(self):
    raise Exception("test")

class MyTestCase(unittest.TestCase):
  def test_if_method_raises_correct_exception(self):
    test_class = TestClass()
    # note that you dont use () when passing the method to assertRaises
    self.assertRaises(Exception, test_class.raises_exception)

Existem muitas respostas aqui. Os shows de código como podemos criar uma exceção, como podemos usar essa exceção em nossos métodos e, finalmente, como você pode verificar em um unittest, as exceções corretas sendo levantadas.

import unittest

class DeviceException(Exception):
    def __init__(self, msg, code):
        self.msg = msg
        self.code = code
    def __str__(self):
        return repr("Error {}: {}".format(self.code, self.msg))

class MyDevice(object):
    def __init__(self):
        self.name = 'DefaultName'

    def setParameter(self, param, value):
        if isinstance(value, str):
            setattr(self, param , value)
        else:
            raise DeviceException('Incorrect type of argument passed. Name expects a string', 100001)

    def getParameter(self, param):
        return getattr(self, param)

class TestMyDevice(unittest.TestCase):

    def setUp(self):
        self.dev1 = MyDevice()

    def tearDown(self):
        del self.dev1

    def test_name(self):
        """ Test for valid input for name parameter """

        self.dev1.setParameter('name', 'MyDevice')
        name = self.dev1.getParameter('name')
        self.assertEqual(name, 'MyDevice')

    def test_invalid_name(self):
        """ Test to check if error is raised if invalid type of input is provided """

        self.assertRaises(DeviceException, self.dev1.setParameter, 'name', 1234)

    def test_exception_message(self):
        """ Test to check if correct exception message and code is raised when incorrect value is passed """

        with self.assertRaises(DeviceException) as cm:
            self.dev1.setParameter('name', 1234)
        self.assertEqual(cm.exception.msg, 'Incorrect type of argument passed. Name expects a string', 'mismatch in expected error message')
        self.assertEqual(cm.exception.code, 100001, 'mismatch in expected error code')


if __name__ == '__main__':
    unittest.main()
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top