在开发软件或Web应用程序时,您必须编写许多功能和一系列任务。这些功能和任务必须正常工作。如果我们在代码中遇到错误,则调试变得困难。
一个好的做法是将我们的代码分为小单位或零件并独立测试以确保它们正常工作。
Python提供了一个名为unittest
的内置模块,该模块允许我们编写和运行单元测试。
始于Unitest
unittest
模块包括许多用于创建和运行测试用例的方法和类。让我们看一个简单的示例,我们在其中使用unittest
模块创建了测试用例。
# basic.py
import unittest
class TestSample(unittest.TestCase):
def test_equal(self):
self.assertEqual(round(3.155), 3.0)
def test_search(self):
self.assertIn("G", "Geek")
首先,我们导入了unittest
模块,这将使我们能够使用用于编写和执行测试用例的类。
定义了TestSample
类的定义,该类从unittest.TestCase
继承,这将使我们能够在我们的测试案例中使用各种断言方法。
我们在TestSample
类中定义了两种测试方法:test_equal
和test_search
。
测试方法test_equal()
测试是否使用assertEqual()
断言方法等于round(3.155)
。
测试方法test_search()
测试是否使用assertIn()
断言方法在字符串"Geek"
中是否存在字符。
要运行这些测试,我们需要在终端中执行以下命令。
python -m unittest basic.py
此命令将启动unittest
作为一个模块,该模块搜索并在basic.py
文件中执行测试。
注意:unittest
模块仅发现并执行以test_
或test
开头的方法。
顺便说一句,这些点代表了一个成功的测试。
我们可以使用unittest.main()
函数,并在测试脚本末尾以以下形式加载并从模块运行测试。
if __name__ == '__main__':
unittest.main()
这将允许我们运行测试文件,在这种情况下为主模块。
更详细的结果
我们可以在终端中使用-v
标志或通过unittest.main()
函数内的参数verbosity=2
来获取测试的详细输出。
常用的断言方法
这是单位测试中最常用的断言方法的列表。
方法 | 检查 |
---|---|
assertEqual(a, b) |
a == b |
assertNotEqual(a, b) |
a != b |
assertTrue(x) |
bool(x) is True |
assertFalse(x) |
bool(x) is False |
assertIs(a, b) |
a is b |
assertIsNot(a, b) |
a is not b |
assertIsNone(x) |
x is None |
assertIsNotNone(x) |
x is not None |
assertIn(a, b) |
a in b |
assertNotIn(a, b) |
a not in b |
assertIsInstance(a, b) |
isinstance(a, b) |
assertNotIsInstance(a, b) |
not isinstance(a, b) |
例子
假设我们有一些代码,并且想使用unittest
模块对其进行单元测试。
# triangle.py
class Triangle:
def __init__(self, base, height):
self.base = base
self.height = height
def area(self):
return 0.5 * self.base * self.height
def perimeter(self, side):
return self.base + self.height + side
代码定义了一个名为Triangle
的类,该类具有一个初始化方法,该方法用实例变量self.base
和self.height
初始化对象。
Triangle
类中还有另外两种方法:area()
和perimeter()
。
area()
方法返回三角形的区域,这是基数和高度的一半(0.5 * self.base * self.height
)。
方法parameter()
接受一个称为side
的参数,并且由于三角参数是其三个侧面的总和,所以base
和height
变量取代了另一个两个方面。
现在,我们可以创建另一个python文件,其中我们将编写一些测试然后执行它们。
# test_sample.py
from triangle import Triangle
import unittest
class TestTriangle(unittest.TestCase):
t = Triangle(9, 8)
def test_area(self):
self.assertEqual(self.t.area(), 36)
def test_perimeter(self):
self.assertEqual(self.t.perimeter(5), 22)
def test_valid_base(self):
self.assertGreater(self.t.base, 0)
def test_valid_height(self):
self.assertGreater(self.t.height, 0)
if __name__ == '__main__':
unittest.main(verbosity=2)
上面的代码从triangle
模块(triangle.py
文件)导入Triangle
类,并导入unittest
模块以编写测试用例。
TestTriangle
类从unittest.TestCase
继承,该类别具有四种测试方法。 Triangle
类是用9
的base
和height
实例化的,并存储在可变t
中。
使用assertEqual()
断言,test_area
方法测试self.t.area()
是否等于预期的36
。
使用assertEqual()
断言,test_perimeter
方法测试self.t.perimeter(5)
是否等于22
。
定义了test_valid_base
和test_valid_height
方法用于测试使用assertGreater()
断言的三角形的基数(self.t.base
)和三角形的高度(self.t.height
)大于0
。
unittest.main(verbosity=2)
方法检索并执行了TestTriangle
类中的测试。我们将获得详细的输出,因为我们使用了verbosity=2
参数。
测试例外
如果您以前使用过assert statements,您会知道,当一个人失败时,它会抛出AssertionError
。同样,每当测试方法失败时,都会提出AssertionError
。
我们可以预先确定代码生成错误的条件,然后测试这些条件以查看它们是否生成错误。这是可以使用assertRaises()
方法。
assertRaises()
方法可以与context manager一起使用,因此我们将以以下形式使用它:
def test_method(self):
with assertRaises(exception_name):
function_name(argument)
考虑以下函数gen_odd()
,该功能通过通过3
递增num
来生成一系列奇数,并仅包含几个检查,其中参数n
必须为int
类型,并且比0
。 >
# odd.py
def gen_odd(n):
if type(n) != int:
raise TypeError("Invalid argument type.")
if n < 0:
raise ValueError("Value must be greater than 0.")
num = 0
while num <= n:
if num % 2 == 1:
print(num)
num += 3
现在,我们将编写测试方法以模拟可能导致上述代码失败的条件。
from odd import gen_odd
import unittest
class OddTestCase(unittest.TestCase):
def test_negative_val(self):
with self.assertRaises(ValueError):
gen_odd(-5)
def test_float_val(self):
with self.assertRaises(TypeError):
gen_odd(99.9)
def test_string_val(self):
with self.assertRaises(TypeError):
gen_odd('10')
if __name__ == '__main__':
unittest.main(verbosity=2)
我们在OddTestCase
类中编写了三种测试方法,以确保在传递无效的参数时,提出了相应的错误。
test_negative_val()
方法断言ValueError
在称为gen_odd(-5)
时提出。
同样,test_float_val()
和test_string_val()
方法断言,当gen_odd(99.9)
和gen_odd('10')
分别称为TypeError
时。
通过上述代码中的所有三个测试,这意味着它们都会引起相应的错误,否则,如果提出了另一个异常,则测试将失败或增加错误。让我们接受测试。
from odd import gen_odd
import unittest
class OddTestCase(unittest.TestCase):
def test_valid_arg(self):
with self.assertRaises(TypeError, msg="Valid argument"):
gen_odd(10)
if __name__ == '__main__':
unittest.main(verbosity=2)
test_valid_arg()
方法中的上述条件不会抛出TypeError
,因为gen_odd()
函数通过有效的参数传递。
上面的测试方法失败并用消息TypeError not raised : Valid argument
提出了AssertionError
。
跳过测试
unittest
使用skip()
Decorator或skipTest()
来跳过任何测试方法或整个测试类,我们需要指定跳过测试的原因。
考虑上一个示例的代码,我们通过添加skip()
装饰器进行了修改。
from odd import gen_odd
import unittest
class OddTestCase(unittest.TestCase):
@unittest.skip("Valid argument")
def test_valid_arg(self):
with self.assertRaises(TypeError):
gen_odd(10)
if __name__ == '__main__':
unittest.main(verbosity=2)
很明显,上面的条件将失败并投掷AssertionError
,因此我们是故意跳过了测试。
如果我们想跳过特定条件,该怎么办?我们可以使用skipIf()
装饰器来实现这一目标,这使我们可以指定条件并跳过测试,如果它是正确的, 。
from odd import gen_odd
import sys
import unittest
class OddTestCase(unittest.TestCase):
@unittest.skipIf(sys.getsizeof(gen_odd(10)) > 10, "Exceeded limit")
def test_memory_use(self):
self.assertTrue(sys.getsizeof(gen_odd(10)) > 10)
print(f"Size: {sys.getsizeof(gen_odd(10))} bytes")
if __name__ == '__main__':
unittest.main(verbosity=2)
上述skipIf()
装饰器中的条件检查gen_odd(10)
的大小是否大于10
字节,如果条件为 true ,则跳过了测试方法test_memory_use()
,否则,测试将执行。 /p>
预期失败
如果我们有一个预期为错误的条件的测试方法或测试类,我们可以使用expectedFailure()
Decorator将其标记为预期的故障而不是检查错误。
from odd import gen_odd
import sys
import unittest
class OddTestCase(unittest.TestCase):
@unittest.expectedFailure
def test_memory_use(self):
self.assertTrue(sys.getsizeof(gen_odd(10)) < 10, msg="Expected to be failed")
print(f"Size: {sys.getsizeof(gen_odd(10))} bytes")
if __name__ == '__main__':
unittest.main(verbosity=2)
我们已经修改了先前的代码,并且在test_memory_use()
方法内检查的条件预计为false,这就是为什么该方法用@unittest.expectedFailure
装饰器进行装饰的原因。
结论
我们可以使用unittest
模块编写和运行测试,以确保代码正常工作。该测试可以导致三个结果之一:好的,失败或错误。
unittest
模块提供了几种用于验证代码的断言方法。
让我们回想一下,我们学到了什么:
-
unittest
模块的基本用法。 -
cli命令运行测试。
-
测试条件是否引起例外。
-
故意跳过测试,当特定条件为真时。
-
将测试标记为预期故障。
如果您喜欢这个 ,您可能会感兴趣的其他文章
How to use assert statements for debugging in Python?
What are the uses of asterisk(*) in Python?
What are __init__ and __new__ methods in Python?
How to implement getitem, setitem and delitem in Python classes?
How to change the string representation of the object using str and repr methods?
How to generate temporary files and directories using tempfile in Python?
Build a custom deep learning model using the transfer learning technique。
这就是目前的全部
保持编码