理解with语句以捕获unittest类中的ValueError

时间:2015-12-16 23:31:51

标签: python python-2.7 unit-testing python-unittest with-statement

一般来说是unittest和Python的新手,在单元测试的教程介绍中遇到了一个例子,其中一个with语句用于捕获ValueError。

正在测试的脚本(invoice_calculator.py)是:

def divide_pay(amount, staff_hours):
    """
    Divide an invoice evenly amongst staff depending on how many hours they
    worked on a project
    """
    total_hours = 0
    for person in staff_hours:
        total_hours += staff_hours[person]

    if total_hours == 0:
        raise ValueError("No hours entered")

    per_hour = amount / total_hours

    staff_pay = {}
    for person in staff_hours:
        pay = staff_hours[person] * per_hour
        staff_pay[person] = pay

    return staff_pay

单元测试包括此函数以捕获staff_hours = None

的边缘情况
import unittest
from invoice_calculator import divide_pay

class InvoiceCalculatorTests(unittest.TestCase):
    def test_equality(self):
        pay = divide_pay(300.0, {"Alice": 3.0, "Bob": 6.0, "Carol": 0.0})
        self.assertEqual(pay, {'Bob': 75.0, 'Alice': 75.0, 'Carol': 150.0})

    def test_zero_hours_total(self):
        with self.assertRaises(ValueError):
            pay = divide_pay(360.0, {"Alice": 0.0, "Bob": 0.0, "Carol": 0.0})

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

关于在with中使用test_zero_hours_total(self)语句,这个语句在如何工作/正在执行方面实际发生了什么?

test_zero_hours_total()函数基本上如下工作(外行人的描述):预期错误应该是ValueError(我们通过将ValueError传递给函数{{1}来做当assertRaises()(会在360.0, {"Alice": 0.0, "Bob": 0.0, "Carol": 0.0}中引发ValueError)作为参数传递给divide_pay()函数时?

2 个答案:

答案 0 :(得分:7)

我不能100%确定你的问题在这里......

TestCase.assertRaises创建一个可以用作上下文管理器的对象(这就是它可以与with语句一起使用的原因)。以这种方式使用时:

with self.assertRaises(SomeExceptionClass):
    # code

上下文管理器的__exit__方法将检查传入的异常信息。如果缺少,将抛出AssertionError导致测试失败。如果异常类型错误(例如,不是SomeExceptionClass的实例),则也会抛出AssertionError

答案 1 :(得分:1)

听起来你明白测试是做什么的。如果assertRaises不存在,您可能会发现如何编写测试很有用。

def test_zero_hours_total(self):
    try:
        pay = divide_pay(360.0, {"Alice": 0.0, "Bob": 0.0, "Carol": 0.0})
    except ValueError:
        # The exception was raised as expected
        pass
    else:
        # If we get here, then the ValueError was not raised
        # raise an exception so that the test fails
        raise AssertionError("ValueError was not raised")

请注意,您不必使用assertRaises作为上下文管理器。您还可以向其传递异常,可调用和可调用的参数:

def test_zero_hours_total(self):
    self.assertRaises(ValueError, divide_pay, 360.0, {"Alice": 0.0, "Bob": 0.0, "Carol": 0.0})