关于if语句:在python中测试elif else条件的有效方法是什么?

Whats the efficient way to test if-elif-else conditions in python

我有很多需要评估的python程序。程序的基本框架包含一组嵌套的if-elif-else语句。我想知道,测试(生成测试用例)来测试程序的最有效方法是什么。

以下是程序的要点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if month == 1:
    if day <= 20:
        print("Capricorn")
    else:
        print("Aquarius")

elif month == 5:
    if day <= 21:
        print("Taurus")
    else:
        print("Gemini")
else:
    if day <= 21:
        print("Sagittarius")
    else:
        print("Capricorn")

它包含更多的elif条件。但我想,你明白了。我想知道,如何生成测试用例或测试覆盖所有条件的代码。


开始编写测试,但包括覆盖工具。您可以安装coverage包并直接使用它,也可以将它与nosezope.testrunner之类的测试运行程序一起使用。

它将告诉您测试是否没有执行任何代码行;在运行测试之后,代码覆盖率报告将为您提供覆盖率百分比和错过的准确行。

您甚至可以使用诸如羽绒被之类的附加工具来可视化可能遗漏的线条,或者与Jenkins或Teamcity等持续集成工具集成,以跟踪覆盖率随时间的变化。


我建议将其设置为桌上驱动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
testtable = {1: (20,"Capricorn","Aquarius"),
             5: (21,"Taurus","Gemini")}

def test(month, day):
    vals = testtable.get(month,
                         (21,"Sagittarius","Capricorn"))  # default
    print( vals[1 + (day <= vals[0])] )

test(1, 20)  # --> Aquarius
test(1, 21)  # --> Capricorn
test(3, 21)  # --> Capricorn
test(3, 22)  # --> Sagittarius
test(5, 21)  # --> Gemini
test(5, 22)  # --> Taurus


对于这样一个有很多排列和组合的问题,您应该创建一个包含所有1个可能的输入和预期输出的矩阵,然后创建一个可以遍历表中所有行的通用测试用例。

为什么是矩阵?因为这是可视化这种场景的最简单方法。例如,如果您收集了24个不同的测试,那么很难有人快速回答这样一个问题:"Do you cover the case of february 29?".对于表中的所有日期和预期值,回答这些问题变得微不足道,而在代码更改时添加额外的测试用例则显得微不足道。

例如,您可以从每个月创建一行开始,因为您的代码似乎基于日历。然后,对于每个月,您将为所有重要条件创建行——每月的第一个、每月的最后一个、月份内的特殊日期(即:如果20个日期给出的结果与21个日期不同,等等)。

您的表在概念上可能如下所示:

1
2
3
4
5
6
7
| # month | day | sign
| 1       | 1   | capricorn
| 1       | 20  | capricorn
| 1       | 21  | aquarius
| 5       | 1   | taurus
| 5       | 21  | taurus
| 5       | 22  | gemini

…等等。然后,编写一个可以读取每一行、调用函数并将输出与预期结果进行比较的测试。

我所说的"全部"并不一定指全部。您需要定义普通案例和边缘案例,但不一定要测试每个可能的日期。


有一些工具,比如coverage.py,可以显示测试用例是否覆盖了所有代码。然而,当一个程序开始有如此多的特殊情况时,最好考虑重新组织它。

在这种情况下,存储数据的方式可以产生所有的差异。这是通过一条if语句来实现所有逻辑的:

1
2
3
4
5
6
def classify(month, day):
    x = 100*month + day
    data = [(120, 218,"Aquarius"), (219, 320, 'Pisces'), (321, 419,"Aries"), (420, 520,"Taurus")]
    for start, end, sign in data:
        if start <= x <= end:
            return sign

现在我们只有一条if语句要测试,生成测试用例变得非常容易。

细节

上面的关键是将日历日期转换成一个可以轻松测试的数字。一个人可以选择一年中的某一天。我选择了100*month+day,因为人类很容易理解和检查。

(上面的占星术数据显然不完整。其余日期和符号的数据在这里。)

如何快速使用python-coverage

下面将生成一个很好的交互式网页,显示测试脚本test.py中包含或不包含的语句:

1
2
3
4
python-coverage erase
python-coverage run test.py
python-coverage html"--omit=/usr/share/*"
firefox ./htmlcov/index.html

HTML输出示例:

enter image description here

这表明覆盖率为100%。


如果你想检查占星术的符号,你可以使用如下的方法:

1
2
3
4
5
6
monthday = month * 100 + day
if monthday <= 120:
    print("Capricorn")
elif monthday <= 219:
    print("Aquarius")
and so on


测试几乎总是基于提供输入和期望输出。但在实现测试之前,需要指定使用代码的接口。它是一个提供API的库吗?然后直接测试API。也就是说,明确定义外部代码将调用的那些函数或方法,然后以与外部代码使用它们的方式测试这些函数或方法。还是命令行工具?然后测试命令行行为。

如何确保测试嵌套条件结构中的所有不同代码路径?用覆盖率分析工具认真思考和评估。