Floating Sunfish
Floating Sunfish

Reputation: 5748

Python: Unit Test That Asserts Custom Exception Is Raised Fails When Try-Except Is Involved

I'm trying to write a unit test that asserts that my nested custom exception is raised by a function.

The below sample code passes:

from unittest import TestCase


class MyClass():
    class MyException(Exception):
        pass


def fail():
    raise MyClass.MyException()


class MyTests(TestCase):
    def test_throwsException(self):
        with self.assertRaises(MyClass.MyException):
            fail()

However, when my raising code involves a try-except, my test fails:

from unittest import TestCase
from enum import Enum


class Weekdays(Enum):
    MONDAY = 'mon'
    TUESDAY = 'tue'
    WEDNESDAY = 'wed'        
    THURSDAY = 'thu'        
    FRIDAY = 'fri'        

    class InvalidValue(Exception):
        pass


def parse(key: str) -> Weekdays:
    try:
        return Weekdays(key)
    except Exception as e:
        raise Weekdays.InvalidValue() from e


class MyTests(TestCase):
    def test_throwsException(self):
        with self.assertRaises(Weekdays.InvalidValue):
            parse('invalid')

It returns the below error:

E
======================================================================
ERROR: test_throwsException (test_main.MyTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "E:\PythonCodes\playground\test_main.py", line 25, in test_throwsException
    with self.assertRaises(Weekdays.InvalidValue):
  File "C:\Users\Admin\AppData\Local\Programs\Python\Python38\lib\unittest\case.py", line 816, in assertRaises
    return context.handle('assertRaises', args, kwargs)
  File "C:\Users\Admin\AppData\Local\Programs\Python\Python38\lib\unittest\case.py", line 187, in handle
    raise TypeError('%s() arg 1 must be %s' %
TypeError: assertRaises() arg 1 must be an exception type or tuple of exception types

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)

I don't quite understand what arg 1 must be an exception type means because I assume that my custom exception is an exception type.

Why is the second version with the try-except failing?

Upvotes: 1

Views: 2602

Answers (1)

juanpa.arrivillaga
juanpa.arrivillaga

Reputation: 96172

The problem is that you've nested the definition of your exception class inside an enum:

class Weekdays(Enum):
    MONDAY = 'mon'
    TUESDAY = 'tue'
    WEDNESDAY = 'wed'        
    THURSDAY = 'thu'        
    FRIDAY = 'fri'        

    class InvalidValue(Exception):
        pass

enum's (through metaclass chicanery) make their class attributes singleton instances of the enum class, with the value you assigned to the attribute in the definition inside the .value of that enum instance. Just like the other class attributes you defined in your enum. So, consider:

In [1]: from enum import Enum

In [2]:
   ...: class Weekdays(Enum):
   ...:     MONDAY = 'mon'
   ...:     TUESDAY = 'tue'
   ...:     WEDNESDAY = 'wed'
   ...:     THURSDAY = 'thu'
   ...:     FRIDAY = 'fri'
   ...:
   ...:     class InvalidValue(Exception):
   ...:         pass
   ...:

In [3]: Weekdays.MONDAY
Out[3]: <Weekdays.MONDAY: 'mon'>

In [4]: Weekdays.MONDAY.value
Out[4]: 'mon'

In [5]: Weekdays.InvalidValue
Out[5]: <Weekdays.InvalidValue: <class '__main__.Weekdays.InvalidValue'>>

In [6]: Weekdays.InvalidValue.value
Out[6]: __main__.Weekdays.InvalidValue

So, you could use:

with self.assertRaises(Weekdays.InvalidValue.value):
    ...

And in similarly, in parse, you need:

raise Weekdays.InvalidValue.value() from e

But you are better off just defining InvalidValue at the module level.

Upvotes: 1

Related Questions