You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为含用户输入的Python函数编写有效单元测试?测试未覆盖问题求解

问题:Driver函数测试未被覆盖率统计纳入及修正方法

被测Driver函数

def driver():
    n = int(input("Enter number of rows: "))
    m = int(input("Enter number of columns: "))
    if n == 0 or m == 0:
        raise ValueError("Invalid grid size.")
    grid = []
    for row in range(n):
        row_i = input("Enter the space-separated cells of row " + str(row + 1) + ": ")
        row_i = list(map(lambda x: int(x), row_i.split()))
        if len(row_i) != m:
            raise IndexError("Invalid input for the given number of columns.")
        if any([cell not in [0, 1] for cell in row_i]):
            raise ValueError("Invalid value of cell, a cell can only have 0 or 1 as a value.")
        grid.append(row_i)
    print("Initial grid: ")
    print_grid(grid)

当前测试代码

class TestGameOfLife(unittest.TestCase):
    def setUp(self):
        self.driver = game_of_life.driver

    @mock.patch('game_of_life.input', create=True)
    def test_driver_invalid_num_rows(self, mocked_input):
        mocked_input.side_effect = ["0", "5"]
        self.assertRaisesRegex(ValueError, "Invalid grid size.", self.driver)

核心问题

当前测试及被测代码均未被覆盖率统计工具纳入,推测测试方式或覆盖率配置存在问题。

修正方案

1. 确保覆盖率工具正确配置

使用coverage.py时,需明确指定要覆盖的源模块,避免遗漏。示例命令:

# 运行测试并统计指定模块的覆盖率
coverage run --source=game_of_life -m unittest test_game_of_life.py
# 生成覆盖率报告
coverage report
# 生成可视化HTML报告(可选)
coverage html

其中--source=game_of_life指定仅统计该模块的代码覆盖率,确保被测代码被纳入统计范围。

2. 优化测试代码逻辑

问题点排查

原测试中,assertRaisesRegex直接传递self.driver(函数对象)的方式,部分覆盖率工具可能无法正确追踪函数执行路径;同时未处理driver依赖的print_grid函数,可能导致测试额外输出或逻辑干扰。

修正后的测试代码

import unittest
from unittest import mock
import game_of_life

class TestGameOfLife(unittest.TestCase):
    def setUp(self):
        self.driver = game_of_life.driver

    @mock.patch('game_of_life.input')
    def test_driver_invalid_num_rows(self, mocked_input):
        mocked_input.side_effect = ["0", "5"]
        # 显式调用被测函数,确保覆盖率工具能追踪执行路径
        with self.assertRaisesRegex(ValueError, "Invalid grid size."):
            self.driver()

    @mock.patch('game_of_life.input')
    def test_driver_invalid_column_count(self, mocked_input):
        mocked_input.side_effect = ["2", "2", "1", "0 1"]
        with self.assertRaisesRegex(IndexError, "Invalid input for the given number of columns."):
            self.driver()

    @mock.patch('game_of_life.input')
    def test_driver_invalid_cell_value(self, mocked_input):
        mocked_input.side_effect = ["1", "2", "1 2"]
        with self.assertRaisesRegex(ValueError, "Invalid value of cell, a cell can only have 0 or 1 as a value."):
            self.driver()

    @mock.patch('game_of_life.input')
    def test_driver_valid_grid(self, mocked_input):
        mocked_input.side_effect = ["2", "2", "1 0", "0 1"]
        # Mock掉print_grid,避免测试输出并聚焦核心逻辑
        with mock.patch('game_of_life.print_grid') as mock_print:
            self.driver()
            # 断言input调用次数符合预期
            self.assertEqual(mocked_input.call_count, 4)
            # 断言print_grid被正确调用
            mock_print.assert_called_once_with([[1,0], [0,1]])

关键说明

  • 显式调用被测函数:将assertRaisesRegex的函数对象参数改为显式调用self.driver(),确保覆盖率工具能正确捕捉函数执行轨迹。
  • Mock依赖函数:对print_grid这类非核心逻辑函数进行mock,避免测试产生无关输出,同时可通过断言验证函数调用正确性。
  • 覆盖所有分支:补充多场景测试,覆盖无效列数、无效单元格值、合法网格等分支,确保测试覆盖完整,覆盖率统计准确。

内容的提问来源于stack exchange,提问作者Sejal Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 09:24:58