简介
在这个实验中,你将学习如何使用 Python 的内置 unittest 模块。该模块提供了一个用于组织和运行测试的框架,这是软件开发中确保代码正确运行的关键部分。
你还将学习创建和运行基本的测试用例,以及测试预期的错误和异常。unittest 模块简化了创建测试套件、运行测试和验证结果的过程。在这个实验中,将创建 teststock.py 文件。
在这个实验中,你将学习如何使用 Python 的内置 unittest 模块。该模块提供了一个用于组织和运行测试的框架,这是软件开发中确保代码正确运行的关键部分。
你还将学习创建和运行基本的测试用例,以及测试预期的错误和异常。unittest 模块简化了创建测试套件、运行测试和验证结果的过程。在这个实验中,将创建 teststock.py 文件。
Python 的 unittest 模块是一个强大的工具,它提供了一种结构化的方式来组织和执行测试。在我们开始编写第一个单元测试之前,让我们先了解一些关键概念。测试固件(Test fixtures)是像 setUp 和 tearDown 这样的方法,它们有助于在测试前准备环境,并在测试后清理环境。测试用例(Test cases)是单独的测试单元,测试套件(Test suites)是测试用例的集合,而测试运行器(Test runners)负责执行这些测试并展示结果。
在这第一步中,我们将为 Stock 类创建一个基本的测试文件,该类已经在 stock.py 文件中定义好了。
stock.py 文件。这将帮助我们了解要测试的 Stock 类。通过查看 stock.py 中的代码,我们可以了解该类的结构、它有哪些属性以及提供了哪些方法。要查看 stock.py 文件的内容,请在终端中运行以下命令:cat stock.py
teststock.py 的新文件了。这个文件将包含我们针对 Stock 类的测试用例。以下是你需要在 teststock.py 文件中编写的代码:## teststock.py
import unittest
import stock
class TestStock(unittest.TestCase):
def test_create(self):
s = stock.Stock('GOOG', 100, 490.1)
self.assertEqual(s.name, 'GOOG')
self.assertEqual(s.shares, 100)
self.assertEqual(s.price, 490.1)
if __name__ == '__main__':
unittest.main()
让我们来分析一下这段代码的关键部分:
import unittest:这行代码导入了 unittest 模块,该模块为在 Python 中编写和运行测试提供了必要的工具和类。import stock:这行代码导入了包含我们 Stock 类的模块。如果没有这个导入,我们就无法在测试代码中访问 Stock 类。class TestStock(unittest.TestCase):我们创建了一个名为 TestStock 的新类,它继承自 unittest.TestCase。这使得我们的 TestStock 类成为一个测试用例类,它可以包含多个测试方法。def test_create(self):这是一个测试方法。在 unittest 框架中,所有的测试方法都必须以 test_ 作为前缀。这个方法创建了一个 Stock 类的实例,然后使用 assertEqual 方法来检查 Stock 实例的属性是否与预期值匹配。assertEqual:这是 TestCase 类提供的一个方法。它用于检查两个值是否相等。如果它们不相等,测试将失败。unittest.main():当直接执行这个脚本时,unittest.main() 将运行 TestStock 类中的所有测试方法并显示结果。teststock.py 文件中编写完代码后,保存它。然后,在终端中运行以下命令来执行测试:python3 teststock.py
你应该会看到类似于以下的输出:
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
输出中的单个点 (.) 表示一个测试已成功通过。如果测试失败,你将看到一个 F 而不是点,同时还会有关于测试中出现问题的详细信息。这个输出有助于你快速确定你的代码是否按预期工作,或者是否有需要修复的问题。
既然你已经创建了一个基本的测试用例,现在是时候扩大你的测试范围了。添加更多的测试将有助于你覆盖 Stock 类的其余功能。这样,你可以确保该类的所有方面都能按预期工作。我们将修改 TestStock 类,以包含对几个方法和属性的测试。
teststock.py 文件。在 TestStock 类中,我们将添加一些新的测试方法。这些方法将测试 Stock 类的不同部分。以下是你需要添加的代码:def test_create_keyword_args(self):
s = stock.Stock(name='GOOG', shares=100, price=490.1)
self.assertEqual(s.name, 'GOOG')
self.assertEqual(s.shares, 100)
self.assertEqual(s.price, 490.1)
def test_cost(self):
s = stock.Stock('GOOG', 100, 490.1)
self.assertEqual(s.cost, 49010.0)
def test_sell(self):
s = stock.Stock('GOOG', 100, 490.1)
s.sell(20)
self.assertEqual(s.shares, 80)
def test_from_row(self):
row = ['GOOG', '100', '490.1']
s = stock.Stock.from_row(row)
self.assertEqual(s.name, 'GOOG')
self.assertEqual(s.shares, 100)
self.assertEqual(s.price, 490.1)
def test_repr(self):
s = stock.Stock('GOOG', 100, 490.1)
self.assertEqual(repr(s), "Stock('GOOG', 100, 490.1)")
def test_eq(self):
s1 = stock.Stock('GOOG', 100, 490.1)
s2 = stock.Stock('GOOG', 100, 490.1)
self.assertEqual(s1, s2)
让我们仔细看看这些测试各自的作用:
test_create_keyword_args:此测试检查你是否可以使用关键字参数创建一个 Stock 对象。它验证对象的属性是否设置正确。test_cost:此测试检查 Stock 对象的 cost 属性是否返回正确的值,该值是通过股数乘以价格计算得出的。test_sell:此测试检查 Stock 对象的 sell() 方法在卖出部分股票后是否正确更新股数。test_from_row:此测试检查 from_row() 类方法是否可以从数据行创建一个新的 Stock 实例。test_repr:此测试检查 Stock 对象的 __repr__() 方法是否返回预期的字符串表示形式。test_eq:此测试检查 __eq__() 方法是否能正确比较两个 Stock 对象是否相等。teststock.py 文件。然后,在终端中使用以下命令再次运行测试:python3 teststock.py
如果所有测试都通过,你应该会看到如下输出:
......
----------------------------------------------------------------------
Ran 7 tests in 0.001s
OK
输出中的七个点代表每个测试。每个点表示一个测试已成功通过。所以,如果你看到七个点,就意味着所有七个测试都通过了。
测试是软件开发中至关重要的一部分,其中一个重要方面是确保你的代码能够正确处理错误情况。在 Python 中,unittest 模块提供了一种便捷的方式来测试特定异常是否按预期抛出。
teststock.py 文件。我们将添加一些用于检查异常的测试方法。这些测试将帮助我们确保代码在遇到无效输入时能正常运行。def test_shares_type(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(TypeError):
s.shares = '50'
def test_shares_value(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(ValueError):
s.shares = -50
def test_price_type(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(TypeError):
s.price = '490.1'
def test_price_value(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(ValueError):
s.price = -490.1
def test_attribute_error(self):
s = stock.Stock('GOOG', 100, 490.1)
with self.assertRaises(AttributeError):
s.share = 100 ## 'share' is incorrect, should be 'shares'
现在,让我们来了解这些异常测试是如何工作的。
with self.assertRaises(ExceptionType): 语句创建了一个上下文管理器。这个上下文管理器会检查 with 块内的代码是否抛出了指定的异常。with 块内抛出了预期的异常,测试就会通过。这意味着我们的代码能够正确检测到无效输入并抛出相应的错误。这些测试旨在验证以下场景:
shares 属性设置为字符串应该抛出 TypeError,因为 shares 应该是一个数字。shares 属性设置为负数应该抛出 ValueError,因为股数不能为负数。price 属性设置为字符串应该抛出 TypeError,因为 price 应该是一个数字。price 属性设置为负数应该抛出 ValueError,因为价格不能为负数。share(注意缺少 's')应该抛出 AttributeError,因为正确的属性名是 shares。teststock.py 文件。然后,在终端中使用以下命令运行所有测试:python3 teststock.py
如果一切正常,你应该会看到输出表明所有 12 个测试都已通过。输出将如下所示:
............
----------------------------------------------------------------------
Ran 12 tests in 0.002s
OK
这十二个点代表你到目前为止编写的所有测试。上一步有 7 个测试,我们刚刚又添加了 5 个。这个输出表明你的代码正在按预期处理异常,这是一个经过充分测试的程序的良好标志。
Python 中的 unittest 模块是一个强大的工具,可让你有效地测试代码。它提供了多种方式来运行特定的测试,或者自动发现并运行项目中的所有测试。这非常有用,因为它能帮助你在测试时专注于代码的特定部分,或者快速检查整个项目的测试套件。
有时,你可能只想运行特定的测试方法或测试类,而不是整个测试套件。你可以通过在 unittest 模块中使用模式选项来实现这一点。这能让你更好地控制执行哪些测试,在调试代码的特定部分时非常方便。
Stock 对象相关的测试:python3 -m unittest teststock.TestStock.test_create
在这个命令中,python3 -m unittest 告诉 Python 运行 unittest 模块。teststock 是测试文件的名称,TestStock 是测试类的名称,test_create 是我们想要运行的特定测试方法。通过运行这个命令,你可以快速检查与创建 Stock 对象相关的代码是否按预期工作。
TestStock 类中的所有测试:python3 -m unittest teststock.TestStock
在这里,我们省略了具体的测试方法名称。因此,这个命令将执行 teststock 文件中 TestStock 类内的所有测试方法。当你想检查 Stock 对象测试用例的整体功能时,这很有用。
unittest 模块可以自动发现并运行项目中的所有测试文件。这省去了你手动指定每个要运行的测试文件的麻烦,特别是在有许多测试文件的大型项目中。
mv teststock.py test_stock.py
unittest 中的测试发现机制会查找遵循 test_*.py 命名模式的文件。通过将文件重命名为 test_stock.py,我们让 unittest 模块更容易找到并运行该文件中的测试。
python3 -m unittest discover
这个命令告诉 unittest 模块自动发现并运行当前目录中所有符合 test_*.py 模式的测试文件。它会遍历目录并执行在匹配文件中找到的所有测试用例。
python3 -m unittest discover -s . -p "test_*.py"
其中:
-s . 指定开始发现测试的目录(在这种情况下是当前目录)。点号 (.) 表示当前目录。如果你想在其他位置搜索测试,可以将其更改为其他目录路径。-p "test_*.py" 是匹配测试文件的模式。这确保只有以 test_ 开头且扩展名为 .py 的文件才会被视为测试文件。你应该会看到和之前一样,所有 12 个测试都运行并通过了。
mv test_stock.py teststock.py
运行测试发现后,我们将文件重命名回原来的名称,以保持实验环境的一致性。
通过使用测试发现功能,你可以轻松运行项目中的所有测试,而无需单独指定每个测试文件。这使测试过程更高效,也更不容易出错。
在本次实验中,你学习了如何使用 Python 的 unittest 模块来创建和运行自动化测试。你通过继承 unittest.TestCase 类创建了一个基本的测试用例,编写了测试来验证类的方法和属性的正常功能,还创建了测试来检查在错误情况下是否抛出了适当的异常。你还学习了如何运行特定的测试以及如何使用测试发现功能。
单元测试是软件开发中的一项基本技能,它能确保代码的可靠性和正确性。编写全面的测试有助于尽早发现 bug,并让你对代码的行为更有信心。在开发 Python 应用程序时,你可以考虑采用测试驱动开发(TDD)的方法,即在实现功能之前先编写测试,这样可以让代码更健壮、更易于维护。