如何使用Unitest模块测试Python代码
#编程 #python #测试 #调试

在开发软件或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_equaltest_search

测试方法test_equal()测试是否使用assertEqual()断言方法等于round(3.155)

测试方法test_search()测试是否使用assertIn()断言方法在字符串"Geek"中是否存在字符。

要运行这些测试,我们需要在终端中执行以下命令。

python -m unittest basic.py

此命令将启动unittest作为一个模块,该模块搜索并在basic.py文件中执行测试。

注意:unittest模块仅发现并执行以test_test开头的方法。

unittest output

顺便说一句,这些点代表了一个成功的测试。

我们可以使用unittest.main()函数,并在测试脚本末尾以以下形式加载并从模块运行测试。

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

这将允许我们运行测试文件,在这种情况下为主模块。

output

更详细的结果

我们可以在终端中使用-v标志或通过unittest.main()函数内的参数verbosity=2来获取测试的详细输出。

output using -v flag

常用的断言方法

这是单位测试中最常用的断言方法的列表。

方法 检查
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.baseself.height初始化对象。

Triangle类中还有另外两种方法:area()perimeter()

area()方法返回三角形的区域,这是基数和高度的一半(0.5 * self.base * self.height)。

方法parameter()接受一个称为side的参数,并且由于三角参数是其三个侧面的总和,所以baseheight变量取代了另一个两个方面。

现在,我们可以创建另一个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类是用9baseheight实例化的,并存储在可变t中。

使用assertEqual()断言,test_area方法测试self.t.area()是否等于预期的36

使用assertEqual()断言,test_perimeter方法测试self.t.perimeter(5)是否等于22

定义了test_valid_basetest_valid_height方法用于测试使用assertGreater()断言的三角形的基数(self.t.base)和三角形的高度(self.t.height)大于0

unittest.main(verbosity=2)方法检索并执行了TestTriangle类中的测试。我们将获得详细的输出,因为我们使用了verbosity=2参数。

TestTriangle output

测试例外

如果您以前使用过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时。

exception checking output

通过上述代码中的所有三个测试,这意味着它们都会引起相应的错误,否则,如果提出了另一个异常,则测试将失败或增加错误。让我们接受测试。

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()函数通过有效的参数传递。

OddTestCase output

上面的测试方法失败并用消息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,因此我们是故意跳过了测试。

output

如果我们想跳过特定条件,该怎么办?我们可以使用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>

slipIf output

预期失败

如果我们有一个预期为错误的条件的测试方法或测试类,我们可以使用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装饰器进行装饰的原因。

expected failure output

结论

我们可以使用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


这就是目前的全部

保持编码