不懂assertRaises with generators + unittest
Do not understand assertRaises with generators + unittest
我不明白哪里出了问题。这是 2 个简单的范围生成器。两者都会在输入大于 MAX 时引发 ValueError。第一个是生成器理解,第二个使用 yield。
MSG = 'Wrong number'
MAX = 20
def test_compr(n, m=MAX):
if n > m:
raise ValueError('{} {} in {}'.format(MSG, n, test_compr.__name__))
return (i for i in range(n))
def test_yield(n, m=MAX):
if n > m:
raise ValueError('{} {} in {}'.format(MSG, n, test_yield.__name__))
i = 0
while i < n:
yield i
i += 1
def main():
n = 30
try:
print(list(test_compr(n)))
except ValueError as e:
print(e)
try:
print(list(test_yield(n)))
except ValueError as e:
print(e)
if __name__ == '__main__':
main()
python3 --version && python t.py
Python 3.7.5
Wrong number 30 in test_compr
Wrong number 30 in test_yield
这按预期工作。
现在进行一些单元测试以确保确实引发了 ValueError。
import unittest
from t import test_compr, test_yield
N_BIG = 30
N_OK = 19
# test generator comprehension
class TestCompr(unittest.TestCase):
def test_ok(self):
tst = N_OK
g = test_compr(tst)
for num in range(tst):
self.assertEqual(num, next(g))
def test_exception(self):
TST = N_BIG
with self.assertRaises(ValueError) as c:
test_compr(N_BIG)
# test yield
class TestYield(unittest.TestCase):
def test_ok(self):
tst = N_OK
g = test_yield(tst)
for num in range(tst):
self.assertEqual(num, next(g))
def test_exception(self):
tst=N_BIG
with self.assertRaises(ValueError) as c:
test_yield(tst)
if __name__ == '__main__':
unittest.main()
> python3 tests.py
Python 3.7.5
..F.
======================================================================
FAIL: test_exception (__main__.TestYield)
----------------------------------------------------------------------
Traceback (most recent call last):
File "tests.py", line 31, in test_exception
test_yield(tst)
AssertionError: ValueError not raised
----------------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (failures=1)
使用生成器理解进行测试会引发错误,但使用生成器进行 yield 测试不会。一旦删除 yield,我就会看到 unittest 在 assertRaises
上产生了正确的结果
已尝试安装最新的 python。同样的问题。
python3.8 --version && python3.8 tests.py
Python 3.8.0
..F.
======================================================================
FAIL: test_exception (__main__.TestYield)
----------------------------------------------------------------------
Traceback (most recent call last):
File "tests.py", line 31, in test_exception
test_yield(tst)
AssertionError: ValueError not raised
----------------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (failures=1)
不确定哪里出了问题以及如何解决这个问题。我从 python3.5 上稍微复杂的代码开始,结果几乎相同。在上面制作样板以在简单的代码上重现问题。
这种看似不一致的行为的原因如下:
test_yield
是生成函数
test_compr
是一个函数,returns 一个生成器
这意味着,当您调用 test_compr
时,函数体将立即被执行,结果 returns 一个生成器(或抛出)。
相比之下,当您调用 test_yield
时,您会收到一个生成器对象。生成器的执行不会在创建时开始,而是在第一次调用 next
函数时开始。因此,您的测试函数应如下所示:
def test_exception(self):
with self.assertRaises(ValueError) as c:
g = test_yield(N_BIG)
next(g)
我不明白哪里出了问题。这是 2 个简单的范围生成器。两者都会在输入大于 MAX 时引发 ValueError。第一个是生成器理解,第二个使用 yield。
MSG = 'Wrong number'
MAX = 20
def test_compr(n, m=MAX):
if n > m:
raise ValueError('{} {} in {}'.format(MSG, n, test_compr.__name__))
return (i for i in range(n))
def test_yield(n, m=MAX):
if n > m:
raise ValueError('{} {} in {}'.format(MSG, n, test_yield.__name__))
i = 0
while i < n:
yield i
i += 1
def main():
n = 30
try:
print(list(test_compr(n)))
except ValueError as e:
print(e)
try:
print(list(test_yield(n)))
except ValueError as e:
print(e)
if __name__ == '__main__':
main()
python3 --version && python t.py
Python 3.7.5
Wrong number 30 in test_compr
Wrong number 30 in test_yield
这按预期工作。 现在进行一些单元测试以确保确实引发了 ValueError。
import unittest
from t import test_compr, test_yield
N_BIG = 30
N_OK = 19
# test generator comprehension
class TestCompr(unittest.TestCase):
def test_ok(self):
tst = N_OK
g = test_compr(tst)
for num in range(tst):
self.assertEqual(num, next(g))
def test_exception(self):
TST = N_BIG
with self.assertRaises(ValueError) as c:
test_compr(N_BIG)
# test yield
class TestYield(unittest.TestCase):
def test_ok(self):
tst = N_OK
g = test_yield(tst)
for num in range(tst):
self.assertEqual(num, next(g))
def test_exception(self):
tst=N_BIG
with self.assertRaises(ValueError) as c:
test_yield(tst)
if __name__ == '__main__':
unittest.main()
> python3 tests.py
Python 3.7.5
..F.
======================================================================
FAIL: test_exception (__main__.TestYield)
----------------------------------------------------------------------
Traceback (most recent call last):
File "tests.py", line 31, in test_exception
test_yield(tst)
AssertionError: ValueError not raised
----------------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (failures=1)
使用生成器理解进行测试会引发错误,但使用生成器进行 yield 测试不会。一旦删除 yield,我就会看到 unittest 在 assertRaises
上产生了正确的结果已尝试安装最新的 python。同样的问题。
python3.8 --version && python3.8 tests.py
Python 3.8.0
..F.
======================================================================
FAIL: test_exception (__main__.TestYield)
----------------------------------------------------------------------
Traceback (most recent call last):
File "tests.py", line 31, in test_exception
test_yield(tst)
AssertionError: ValueError not raised
----------------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (failures=1)
不确定哪里出了问题以及如何解决这个问题。我从 python3.5 上稍微复杂的代码开始,结果几乎相同。在上面制作样板以在简单的代码上重现问题。
这种看似不一致的行为的原因如下:
test_yield
是生成函数test_compr
是一个函数,returns 一个生成器
这意味着,当您调用 test_compr
时,函数体将立即被执行,结果 returns 一个生成器(或抛出)。
相比之下,当您调用 test_yield
时,您会收到一个生成器对象。生成器的执行不会在创建时开始,而是在第一次调用 next
函数时开始。因此,您的测试函数应如下所示:
def test_exception(self):
with self.assertRaises(ValueError) as c:
g = test_yield(N_BIG)
next(g)