我实现的对不对?

现在,大家已经完成了第一个题目。这个题目当然是很简单的,但之后随着知识的增多,我们要完成的题目也越发复杂。那么有没有什么方法能检验我们写的代码对不对呢?

手动测试我的代码

回想一下,doctest描述了“当我们在交互模式下这样输入代码,就会得到这样的结果”,那么我们是不是可以打开交互模式,按照doctest的描述输入代码,然后观察结果是不是一致的呢?

答案是肯定的。我们首先打开终端,然后使用cd命令把当前目录切换到lab00.py所在的目录下(回忆一下实验讲义前面教你如何切换当前目录)。然后输入python -i lab00.py。这个命令长得和我们在前面学过的python lab00.py很像,但多了个-i-i是告诉Python,在使用python lab00.py执行完lab00.py后,进入交互模式。而开发Python的程序员之所以采用i这个字母,是因为它是interactive(交互)的首字母,方便记忆。现在让我们执行python -i lab00.py。你应该能在终端上看到下面的效果:

$ python -i lab00.py
>>>

接下来的过程就和交互模式一模一样了。你可以对照着doctest,输入twenty_twenty_one(),然后就会得到

$ python -i lab00.py
>>> twenty_twenty_one()
2021
>>>

这和doctest描述的一模一样,所以你现在可以松口气,相信自己的代码是对的了。如果发现不一样,你就需要看看自己哪里出问题了。

等一等!刚才其实有一个概念的陷阱。现在让我们思考一个问题:为什么说,经过刚才这样一个过程,我们就有理由相信我们的代码是对的呢?

实际上,“相信自己的代码是对的”是一个错误的说法!准确的说,大家只能有理由相信“自己写的代码和doctest所预期的相符合”,进而推论,自己的代码可能是对的。

其实,像这样一个比较代码的实际执行结果和预期结果的过程就叫做测试。这也是“doctest”中test的中文含义。测试不能保证“代码是对的”,只能保证代码在多大程度上与预期相符合。与预期符合得程度越高,我们就更加有信心来相信,我们的代码是对的。

实际上,还有一种检查程序正确的方式叫做形式化验证(Formal Verification),通过规则和逻辑(而非测试样本)来判断程序的正确性。这正是冯新宇老师研究的领域之一。在你之后的学习过程中可能还会进一步接触相关内容。

助教会如何测试我的代码?

对于助教来说,很难一一审查每个人的代码来验证大家写得正不正确,所以我们会采用测试的方法来给大家评分。像我们发布给同学的doctest一般都写得比较简单,所以那些写得不正确的代码也有可能通过doctest,比如像下面这段代码,本来助教希望大家写return a + b,但有个小机灵鬼直接返回3试图蒙混过关:

def add(a, b):
    """return the sum of a and b

    >>> add(1, 2)
    3
    """
    return 3

但如果我们的测试写得越“详细”,写得不正确的代码就越难蒙混过关。比如像现在:

def add(a, b):
    """return the sum of a and b

    >>> add(1, 2)
    3
    >>> add(3, 4)
    7
    """
    return 3

因此,助教们会设置一个在线测评网站,用更加“详细”的测试来给大家评分,尽可能防止不正确的代码蒙混过关。我们会在下一节中教大家如何在网站上提交作业,让助教测试你的代码。

本节关于测试的介绍,如果大家理解得不清楚也没关系。甚至这一节提到的方法不用也没关系。重点是记住下一节要讲的提交作业的方法,并知道如何查看自己写的代码拿了多少分。

使用Ok进行自动化测试

随着大家做的题目越来越多、越来越复杂,同学们可能会发现,每当自己写了一段新的代码,就要重新走上面一套doctest的流程,然后嫌手动输入表达式很麻烦。

Python的doctest模块可以自动执行文件中的所有doctest测试,包括你没有完成的函数也会一起测试。 我们可以在终端中输入python -m doctest lab00.py,这个命令的内部原理基本上和你之前手动执行的过程一模一样。

我们推荐大家使用Ok客户端来进行自动化测试,ok客户端会帮助我们测试、备份、提交作业。

首先,我们需要登录自己的OJ账号。 在终端中运行python ok --authenticate,输入自己的OJ用户名和密码完成登录。

$ python ok --authenticate
Performing authentication
Please input your NJU ID:    # 输入学号并按下回车
Please input your password:  # 输入密码(不会显示在终端上)并按下回车
Authenticated as ......

接着,运行python ok,Ok会和上面的代码一样执行所有的doctest或其他配置好的复杂测试,并在第一次出错时停止执行。 如果我们使用-i参数,如python ok -i,那么出错时就会进入交互模式,方便我们进行排查。 当Ok完成测试后,它会自动帮我们上传一份代码的备份,我们可以在在线测评网站上查看并获取备份。

$ python ok
=====================================================================
Assignment: lab00
OK, version NJU-SICP-v1.2.0
=====================================================================

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Running tests

---------------------------------------------------------------------
Doctests for add

>>> from lab00 import *
>>> twenty_twenty_one()

# Error: expected
#     2021
# but got


---------------------------------------------------------------------
Test summary
    0 test cases passed before encountering first failed test case