Notes of testing and debugging

551 查看

今天复习了MIT计算机导论的lecture7,关于测试和调试的概念有了一些基本了解,记录下来怕忘记。

测试基础

  1. The most important thing to say about testing is that its purpose is to show that bugs exist, not to show that a program is bug-free.

  2. The key to testing is finding a collection of inputs, called a test suite, that has a high likelihood of revealing bugs, yet does not take too long to run.
    Test suites based on exploring paths through the code called glass-box testing. based on exploring paths through the specification(说明书) called black-box testing.

  1. in black-box testing, we must consider two kinds of conditions

    • typical conditions through the specification
    • boundary conditions
  2. in glass-box testing, the test suite is path-conmplete, there are a few rules of thumb thatare usually worth following:
    2.1 Exercise both branches of all if statements.
    2.2 Make sure that each except clause is executed.
    For each for loop, have test cases in which
    2.3 The loop is not entered (e.g., if the loop is iterating over theelements of a list, make sure that it is tested on the empty list)
    2.4 The body of the loop is executed exactly once, and
    The body of the loop is executed more than once.

2.5 For each while loop,

  • Look at the same kinds of cases as when dealing with for loops,and
  • Include test cases corresponding to all possible ways of exiting the loop. For example, for a loop starting with
    while len(L) > 0 and not L[i] == efind cases where the loop exits because len(L) is greater than
    zero and cases where it exits because L[i] == e.

2.6 For recursive functions, include test cases that cause the function toreturn with no recursive calls, exactly one recursive call, and more thanone recursive call.

测试过程

  1. 单元测试(unit test)
  2. 集成测试(integration test)
  3. 反复上述过程多次
    一个有用的方法是建立一段测试驱动(test driver),即一段用来测试的代码

调试

我们可以把调试看作一个检索问题,如果某段代码错误,我们需要找出一个解释,到底是哪个部分造成代码产生了不符合我们预期的结果,我们通常使用二分检索和打印语句来完成这一过程。
具体步骤是,如果某段代码错误,我们在这段代码的中间位置放一个打印语句,若是打印语句不符合预期,那我们知道这段代码在打印语句之前就产生了错误,那我们就关注打印语句之前的那一部分,在这部分的中间放一个打印语句,运行观察代码,反之,则观察打印语句之后的部分,如此循环往复,直到调试成功。

一个常犯却不易被发现的错误--aliasing bug

如下列代码
def isPal(x):

assert type(x) == list
temp = x
temp.reverse
if temp == x:
    return True
else:
return False

def silly(n):

result = []
for i in range(n):
    elem = raw_input('Enter element: ')
    result.append(elem)
if isPal(result):
    print('Yes')
else:
    print('No')

在这段代码中,有两个错误,当我们要调用一个列表的方法时,形式应该是a.reverse(), 若没有(),我们只会得到reverse这个函数,不能使列表转向。还有一个错误就是当我们执行到 if isPal(result) 语句时,我们假设result = [1, 2, 3], result 这个变量指向列表对象[1, 2, 3],当后来执行到 temp = x(temp = result)时,temp同样指向列表对象[1, 2, 3],当一个对象有多个引用的时候,并且引用有不同的名称,我们称这个对象有别名(aliase),因此当temp被转向时,x同样被转向,因为他们两个指向同一个对象,这种情况很容易发生别名错误,不过只发生在可变对象。改好的代码如下
def isPal(x):

assert type(x) == list
temp = x[:]
temp.reverse.()
if temp == x:
    return True
else:
return False

def silly(n):

result = []
for i in range(n):
    elem = raw_input('Enter element: ')
    result.append(elem)
if isPal(result):
    print('Yes')
else:
    print('No')