
单元测试属于软件开发进程里极为关键的一部分内容, 这是由于它能够保证代码于运行之际是准确无误的, 而且在后续的开发进程里面不会引进新的差错。开展单元测试主要借助几个工具以及库, 像是、还有nose。以下是单元测试脚本的具体操作办法:
- 先是使用库, 接着还是使用库, 然后运用mock对象去进行测试, 最后要编写高质量的测试用例。
我们以详细描述使用库:
它是自带的, 属于一个单元测试框架, 此框架提供了一种方式来编写以及运行测试, 这种方式具备结构化与标准化的特性。若要使用库去进行单元测试, 那么首先得编写测试类, 并且要在这个类里定义测试方法。下面在此呈现的是一个简单的示例:
python
import unittest
def add(a, b):
return a + b
class TestMathFunctions(unittest.TestCase):
def test_add(self):
self.assertEqual(add(1, 2), 3)
self.assertEqual(add(-1, 1), 0)
self.assertEqual(add(-1, -1), -2)
if __name__ == '__mn__':
unittest.main()
在这个示例当中, 我们对名叫add的函数进行了定义, 之后运用编写的方式弄出了一个测试类, 并且在这个测试类里定义了一个测试方法。借助调用self., 我们能够去查验add函数输出的情况是不是契合预期。最终, 借助.main()使所有的测试获得运行。
一、使用库
这是一个自身携带的单元测试框架, 其具备十分丰富的功能用以支撑测试类, 以及测试方法。该库的核心部分乃是类, 我们能够通过继承这个类去撰写我们的测试用例。
- 编写测试用例
进行编写测试用例的首个步骤是开展导入库的操作, 之后要去创建一个继承的测试类。于该测试类里面, 能够对许多测试实例对象进行定义。每一个测试实例对象是以test_作为开始的。如下是一个简易的例子:
python
import unittest
def multiply(a, b):
return a * b
class TestMathFunctions(unittest.TestCase):
def test_multiply(self):
self.assertEqual(multiply(3, 4), 12)
self.assertEqual(multiply(-1, 5), -5)
self.assertEqual(multiply(-2, -2), 4)
if __name__ == '__main__':
unittest.main()
在这个示例里头, 咱们定义了一个函数, 还编写了一个测试类, 这个测试类之中涵盖了一个测试方法。借由调用self. , 我们能够核查函数的输出是否契合预期。
- 使用setUp和方法
在其内部, 我们能够运用setUp以及一种方法去针对测试之前的筹备事宜和测试完毕事后清理之举措进行操作。setUp这个方法会于每单个测试方法着手运行之前予以实施作用, 然而那种方法会在每单个测试方法运行结束之后才展开运行行动。以下归属于一个示例情况:
python
import unittest
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
class TestMathFunctions(unittest.TestCase):
def setUp(self):
self.a = 10
self.b = 5
def tearDown(self):
pass
def test_divide(self):
self.assertEqual(divide(self.a, self.b), 2)
with self.assertRaises(ValueError):
divide(self.a, 0)
if __name__ == '__main__':
unittest.main()
在这个示例当中, 我们借助setUp方法, 于每个测试方法运行之前, 对一些变量a以及b进行了初始化。随后, 在对应的方法内, 利用这些变量展开测试。此方法当前并无实际的用途, 然而它能够被用来清理测试进程里所创建的资源。
二、使用库
这是一个流行的单元测试框架, 它具备比某框更灵活强大的特性, 像更好的断言、参数化测试以及插件系统这些特性虽不被某框承认然而被它支持。
- 编写测试用例
去编写测试用例这件事是十分简单的, 仅仅只要去编写一个是以test_作为起始的函数, 并且还要运用语句来开展断言。下面是给出的一个简单的示例:
python
def subtract(a, b):
return a - b
def test_subtract():
assert subtract(10, 5) == 5
assert subtract(-1, -1) == 0
assert subtract(0, 0) == 0
在这个示例里头, 我们对一个函数予以了定义, 并且编写了一个用于测试的函数。于该函数当中, 我们借助语句去查验函数所得出的输出是不是契合预期。
- 使用
这是一款具备强大能力的功用, 它准许我们于测试方法开展之前以及之后去执行一些代码;我们能够运用@.装饰器予以界定, 并且在测试方法之内加以运用;如下便为一个实例:
python
import pytest
@pytest.fixture
def input_data():
return 10, 5
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
def test_divide(input_data):
a, b = input_data
assert divide(a, b) == 2
with pytest.raises(ValueError):
divide(a, 0)
于这个示例里头, 我们借助@.界定了一个, 其返回了某些输入数据。于函数当中, 我们经由参数运用这个, 随后予以测试。
三、使用mock对象进行测试
有些时候, 我们所需测试的代码会依赖于诸如数据库以及网络请求等之类的外部资源, 此时, 我们能够借助mock对象去模拟这些外部资源, 进而开展单元测试, 具有强大的mock功能的.mock模块提供了便利标点符号。
- 使用
它属于.mock模块里的某一个类, 这个类能够对任意对象的行为予以模拟。这是下面的一个示例:
python
from unittest.mock import MagicMock
class Database:
def connect(self):
pass
def fetch_data(db):
db.connect()
return "data"
def test_fetch_data():
db = MagicMock()
result = fetch_data(db)
db.connect.assert_called_once()
assert result == "data"
在该示例里头, 我们设定了一个类, 还设定了一个函数。此函数依托于类的方法。为了去做单元测试, 我们运用了模拟对象, 并且核查方法是否被调用了一回。
- 使用patch装饰器
patch, 属于.mock模块里的一个装饰器, 它能够临时去替换某一个对象, 下面是一个示例。
python
from unittest.mock import patch
class Network:
def request(self, url):
pass
def get_status(url):
net = Network()
response = net.request(url)
return response.status_code
@patch.object(Network, 'request')
def test_get_status(mock_request):
mock_request.return_value.status_code = 200
status = get_status("http://example.com")
mock_request.assert_called_once_with("http://example.com")
assert status == 200
这里有个示例, 示例之中, 我们定下了一个类, 还弄出了一个函数。函数是依靠着类的方法的。为了去做单元测试这般的事, 我们动用了patch装饰器, 临时把类的方法给替换掉, 接着去查看它是不是被正确地调用了。
四、编写高质量的测试用例
编撰具备高水准的测试用例乃保障代码质量以及稳定性的关键所在, 这里是一些关于编撰具备高水准测试用例的建议。
- 覆盖所有边界情况
当我们着手于编写那测试用例之际, 我们应当尽可能地去覆盖掉一切的边界情况, 这里面涵盖着正常情况, 还有异常情况以及极端情况。下面是这样的一个示例, 此示例为:
python
def add(a, b):
return a + b
def test_add():
assert add(1, 2) == 3 # 正常情况
assert add(-1, 1) == 0 # 边界情况
assert add(0, 0) == 0 # 边界情况
assert add(1.5, 2.5) == 4.0 # 极端情况
针对这个示例, 我们撰写了一个测试函数, 该函数涵盖了add函数的正常状况, 还包括其边界情形, 以及极端状况。
- 使用参数化测试
有这样一种测试方法, 它是参数化测试, 属于有效的测试方式, 其准许我们借由不一样的参数去运转同一个测试函数。对与之相配合情况给予支持参数化测试, 下面呈现出一个示例:
python
import pytest
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(-1, 1, 0),
(0, 0, 0),
(1.5, 2.5, 4.0)
])
def test_add(a, b, expected):
assert add(a, b) == expected
在这个示例里头, 我们应用@.mark.装饰器去定义参数化测试, 借由这样的途径, 我们能够削减重复代码, 并且提升测试覆盖率。
- 使用测试驱动开发(TDD)
一种叫做测试驱动开发的软件开发方法, 它着重讲在编写代码以前先去编写测试用例, 测试驱动开发的核心思想是"红-绿-重构"这个循环, 也就是先去编写一个会失败的测试用例(红), 接着编写代码让测试能够通过(绿), 最后针对代码开展重构, 以下是一个示例。
python
import unittest
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
class TestMathFunctions(unittest.TestCase):
def test_factorial(self):
self.assertEqual(factorial(0), 1)
self.assertEqual(factorial(1), 1)
self.assertEqual(factorial(5), 120)
self.assertEqual(factorial(10), 3628800)
if __name__ == '__main__':
unittest.main()
在这一示例里头, 我们先是创作了一个失败情形下的测试案例, 接着撰写相应函数以使测试能够顺利通过, 最终针对代码动工进行重构, 借由这样的一种方法, 我们能够保障代码于开发进程之中一直维持正确无误。
五、集成测试和持续集成
除单元测试外, 集成测试、持续集成(CI)亦是保障代码质量的关键方式。集成测试意为对多个模块或组件间交互状况作出测试, 持续集成指把代码频繁整合融进主干分支, 且借由自动化测试核实代码正确无差错。
- 编写集成测试
展开集成测试常常会关联到不少模块或者一些组件, 所以呢就使得其需要更为复杂的那种测试环境。下面是这么一个简单的示例呐:
python
import unittest
class Database:
def connect(self):
return "Connected"
class Service:
def __init__(self, db):
self.db = db
def get_data(self):
if self.db.connect() == "Connected":
return "Data"
class TestIntegration(unittest.TestCase):
def test_service(self):
db = Database()
service = Service(db)
self.assertEqual(service.get_data(), "Data")
if __name__ == '__main__':
unittest.main()
于此示例当中, 我们界定了一个类, 还界定了一个类, 接着编写了一个集成测试类。借由这般方式, 我们能够去测试哎, 和那个 和 之间的交互。
- 使用持续集成工具
具备持续集成能力之力的相关工具, 像是可以简称为CI之类的那类工具, 能够助力我们达成自动化测试以及部署流程。以下呈现的是一个借助具有持续集成功能的工具来开展持续集成的比如像这样的示例配置文件:
yaml
name: Python CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests
run: |
python -m unittest discover
在此示例里, 我们界定了一个配置文件。此配置文件是用以会在每次代码推送之际, 自动去运行单元测试的。借由这样的方式, 我们能够保证代码于每个修改之后, 均可通过测试。
六、代码覆盖率和测试报告
代码覆盖率, 是用于衡量测试覆盖范围的一项颇为重要的指标, 借助它, 便能够帮我们找出那些尚未被测试触及到的代码, 存在用于辅助测量代码覆盖率的.py库, 它还能够生成极为详尽且具体的测试报告。
首先,我们需要安装.py库:
bash
pip install coverage
- 使用.py测量代码覆盖率
随后, 我们能够运用.py去开展测试, 进而测量代码覆盖率。这儿有一个例子:
bash
coverage run -m unittest discover
coverage report
coverage html
在这个示例当中, 我们借助run命令去运行测试, 并且运用命令来生成覆盖率报告。我们能够用到html命令, 以此来生成在浏览器里能查看用到HTML格式的覆盖率报告。
- 集成代码覆盖率工具
代码覆盖率工具, 我们仍旧能够把它集成进持续集成工具里, 从而在每一回代码推送之际, 能够自动开展覆盖率的测量。下面给出的是, 一个借助 来实施代码覆盖率测量的示例配置文件:
yaml
name: Python CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install coverage
- name: Run tests with coverage
run: |
coverage run -m unittest discover
coverage report
coverage html
- name: Upload coverage report
uses: actions/upload-artifact@v2
with:
name: coverage-report
path: htmlcov/
于这个示例里头, 我们呢, 在配置文件当中添加了.py的安装以及使用的步骤, 借由这样的一种方式, 我们能够在每一回代码推送之际, 自动去测量代码覆盖率, 此后还会生成覆盖率报告。
经由以上步骤, 我们能够运用mock对象、集成测试、持续循环工具去开展脚本的单元测试。着手编写具备高层次质量的考验事例, 将全部边界状况全都涵盖住, 参照用参化剖析与开发驱动检定方法 , 能够实效提升代码质量以及稳固程度。与此同时经由衡量代码覆盖比率以及形成考验报告, 能够助力我们察觉到那些未被测试的代码, 进而更进一步提升测试覆盖百分数。
相关问答FAQs:
如何为脚本编写有效的单元测试?
对编写有效的单元测试而言, 需遵循一些基本原则, 首先, 要确保每个测试函数都对准一个特定功能或模块, 使用的库能够助力你安排测试用例, 运用语句去核实验证输出是不是符合预期, 此外, 维持测试的独立性是关键所在, 要保证每个测试相互之间不存在依赖关系, 从而能够单独进行运行以及调试。
在中使用哪些工具来进行单元测试?
提供了好些工具, 用于开展单元测试, 里头最常用的是和, 是那种内置的测试框架, 适配基础的单元测试需求, 而呢, 给出了更具灵活性的功能, 还有更简洁的语法, 能够更从容地应对复杂的测试场景, 挑选合适的工具, 能够提升测试效率, 以及代码质量。
如何确保单元测试的覆盖率?
能够借助.py等相关工具达成单元测试覆盖率的确保。该工具能够对代码予以分析。进而展现出哪些部分被测试所覆盖。哪些部分没有被测试覆盖。于理想状况之下。尽可能实现100%的覆盖率。然而更为重要的事宜则是关注关键逻辑以及功能。保证这些关键逻辑和功能得到充分的测试。定期展开审查以及更新测试用例同样有助于维护良好的覆盖率。
