【问题标题】:Python: Unit Test That Asserts Custom Exception Is Raised Fails When Try-Except Is InvolvedPython:当涉及 Try-Except 时,断言自定义异常被引发的单元测试失败
【发布时间】:2020-11-03 06:34:35
【问题描述】:

我正在尝试编写一个单元测试,断言我的嵌套自定义异常是由函数引发的。

以下示例代码通过:

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()

但是,当我的提升代码涉及 try-except 时,我的测试会失败:

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')

它返回以下错误:

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)

我不太明白arg 1 must be an exception type 的含义,因为我假设我的自定义异常异常类型。

为什么第二个版本的 try-except 失败了?

【问题讨论】:

  • 你为什么要定义你的异常类inside你的枚举?这使得您从Weekdays.InvalidValue 一个枚举 访问的内容具有该类属性的.value...就像您在枚举上定义的任何其他类属性一样。只需将其从枚举中删除即可。
  • 嗯,我明白了!这确实解决了这个问题。似乎枚举类中不能有嵌套类。您可以将此添加为答案,以便我接受吗?顺便说一句,我出于组织目的嵌套了我的自定义异常。
  • 嗯,他们可以。但是嵌套一个类并没有什么特别之处,它相当于my_attribute = SomeClass。一般来说,嵌套类不是 Python 中的常见模式。它确实没有提供太多优势。代码组织的基本单位是module
  • 我最近在 python 中看到了一些关于嵌套类的问题。我认为对于嵌套类的工作方式存在一个普遍的误解,因为它们根本不从它们所在的类继承任何东西。也许有人可以纠正我,但我能看到嵌套类的唯一好处是组织命名空间。然后,当外部类更改属性访问的工作方式时,即使这样也可能会消失。
  • @Aaron 是的,将一个类定义粘贴在另一个类定义的主体中的唯一效果是生成的类对象位于它嵌套的类的命名空间中. 当然,对于元类,各种诡计都可能发生,如上所示。我曾经使用过一次或两次嵌套类,我相信它们在 Django 中使用过(它严重依赖于元类魔法)。但这几乎总是比它的价值更麻烦。如果一个类是“私有的”并且不应该公开,只需将它放在模块级别并使用单下划线,这就是 Pythonic 约定。

标签: python unit-testing exception try-catch


【解决方案1】:

问题是您将异常类的定义嵌套在枚举中:

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

    class InvalidValue(Exception):
        pass

枚举(通过元类诡计)使它们的类属性枚举类的单个实例,具有您在该枚举实例的.value 内的定义中分配给属性的值。就像您在枚举中定义的其他类属性一样。所以,考虑一下:

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

所以,你可以使用:

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

同样,在parse 中,您需要:

raise Weekdays.InvalidValue.value() from e

但你最好只在模块级别定义InvalidValue

【讨论】:

    猜你喜欢
    • 2018-04-19
    • 2011-06-11
    • 2020-01-18
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多