Python学习笔记16:测试基础

文章介绍了测试驱动编程的概念,包括先编写测试后编码的流程,并展示了如何使用Python的unittest和doctest模块进行单元测试。此外,文章还提到了代码质量检查工具PyChecker和PyLint,以及性能分析模块profile,强调了它们在确保代码质量和性能方面的作用。
摘要由CSDN通过智能技术生成

测试基础

先测试再编码

测试驱动的编程

测试四部曲

  1. 确定需要实现的新功能。可将其记录下来,再为之编写一个测试。
  2. 编写实现功能的框架代码,让程序能够运行(不存在语法错误之类的问题),但测试依然无法通过。
  3. 编写让测试刚好能够通过的代码。
  4. 改进(重构)代码以全面而准确地实现所需的功能,同时确保测试依然能够成功。

测试工具

  • unittest:一个通用的测试框架。
  • doctest:一个更简单的模块,是为检查文档而设计的,但也非常适合用来编写单元测试。

doctest

#my_math.py
def square(x): 
    ''' 
    计算平方并返回结果
    >>> square(2) 
    4
    >>> square(3) 
    9
    ''' 
    return x * x
    
if __name__=='__main__': 
    import doctest, my_math 
    doctest.testmod(my_math)

运行结果如下:

# Window PowerShell
PS C:\Users\15735\Desktop> python my_math.py
# -v(verbose,意为详尽)
PS C:\Users\15735\Desktop> python my_math.py -v
Trying:
    square(2)
Expecting:
    4
ok
Trying:
    square(3)
Expecting:
    9
ok
1 items had no tests:
    my_math
1 items passed all tests:
   2 tests in my_math.square
2 tests in 2 items.
2 passed and 0 failed.
Test passed.

PS:

  • 需要到cmd执行命令 python my_math.py
  • if __name__ 而不是name
  • 4和9后面不能有空格,否则会报错,如下
PS C:\Users\15735\Desktop> python my_math.py
**********************************************************************
File "C:\Users\15735\Desktop\my_math.py", line 4, in my_math.square
Failed example:
    square(2)
Expected:
    4
Got:
    4
**********************************************************************
File "C:\Users\15735\Desktop\my_math.py", line 6, in my_math.square
Failed example:
    square(3)
Expected:
    9
Got:
    9
**********************************************************************
1 items had failures:
   2 of   2 in my_math.square
***Test Failed*** 2 failures.

unittest

一个使用框架unittest的简单测试

# test_my_math.py
import unittest, my_math 
class ProductTestCase(unittest.TestCase): 

    def test_integers(self): 
        for x in range(-10, 10): 
            for y in range(-10, 10): 
                p = my_math.product(x, y) 
                self.assertEqual(p, x * y, 'Integer multiplication failed') 
    
    def test_floats(self): 
        for x in range(-10, 10): 
            for y in range(-10, 10): 
                x = x / 10 
                y = y / 10 
                p = my_math.product(x, y) 
                self.assertEqual(p, x * y, 'Float multiplication failed') 
        
if __name__ == '__main__': unittest.main()
# my_math.py
def product(x, y): 
    pass

执行test_my_math.py,输出如下:

## FF 表示两次失败
PS C:\Users\15735\desktop> python test_my_math.py
FF
======================================================================
FAIL: test_floats (__main__.ProductTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\15735\desktop\test_my_math.py", line 16, in test_floats
    self.assertEqual(p, x * y, 'Float multiplication failed')
AssertionError: None != 1.0 : Float multiplication failed

======================================================================
FAIL: test_integers (__main__.ProductTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\15735\desktop\test_my_math.py", line 8, in test_integers
    self.assertEqual(p, x * y, 'Integer multiplication failed')
AssertionError: None != 100 : Integer multiplication failed

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=2)

PS:

如果你定义了方法setUp和tearDown,它们将分别在每个测试方法之前和之后执行。你可使用这些方法来执行适用于所有测试的初始化代码和清理代码,这些代码称为测试夹具(test fixture)。

修改示例

def product(x, y): 
    return x * y

输出如下:

## ..表示测试
PS C:\Users\15735\desktop> python test_my_math.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK

超越单元测试

源代码检查和性能分析:单元测试可让程序管用,源代码检查可让程序更好,而性能分析可让程序更快。

使用PyChecker和PyLint检查源代码

要使用PyChecker来检查文件,可运行这个脚本并将文件名作为参数,如下所示:

pychecker file1.py file2.py …

使用PyLint检查文件时,需要将模块(或包)名作为参数:

pylint module

使用模块subprocess调用外部检查器

# pylint 指定了开关-rn(其中n表示no)以关闭报告,这意味着将只显示警告和错误。
import unittest, my_math 
from subprocess import Popen, PIPE 

class ProductTestCase(unittest.TestCase):

    #在这里插入以前的测试
    #def test_with_PyChecker(self): 
    #    cmd = 'pychecker', '-Q', my_math.__file__.rstrip('c') 
    #    pychecker = Popen(cmd, stdout=PIPE, stderr=PIPE) 
    #    self.assertEqual(pychecker.stdout.read(), '') 
    
    def test_with_PyLint(self): 
        cmd = 'pylint', '-rn', 'my_math' 
        pylint = Popen(cmd, stdout=PIPE, stderr=PIPE) 
        self.assertEqual(pylint.stdout.read(), '')
        
if __name__ == '__main__': unittest.main()
""" 
一个简单的数学模块
""" 
__revision__ = '0.1' 

def product(factor1, factor2): 
    'The product of two numbers' 
    return factor1 * factor2

运行结果如下:

PS C:\Users\15735\desktop> python .\test_my_math.py
FD:\Program Files\Python\lib\subprocess.py:1072: ResourceWarning: subprocess 15416 is still running
  _warn("subprocess %s is still running" % self.pid,
ResourceWarning: Enable tracemalloc to get the object allocation traceback
D:\Program Files\Python\lib\unittest\case.py:613: ResourceWarning: unclosed file <_io.BufferedReader name=3>
  outcome.errors.clear()
ResourceWarning: Enable tracemalloc to get the object allocation traceback
D:\Program Files\Python\lib\unittest\case.py:613: ResourceWarning: unclosed file <_io.BufferedReader name=4>
  outcome.errors.clear()
ResourceWarning: Enable tracemalloc to get the object allocation traceback

======================================================================
FAIL: test_with_PyLint (__main__.ProductTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\15735\desktop\test_my_math.py", line 15, in test_with_PyLint
    self.assertEqual(pylint.stdout.read(), '')
AssertionError: b'' != ''

----------------------------------------------------------------------
Ran 1 test in 0.260s

FAILED (failures=1)

性能分析

标准库包含一个卓越的性能分析模块profile,还有一个速度更快C语言版本,名为cProfile。这个性能分析模块使用起来很简单,只需调用其方法run并提供一个字符串参数。

>>> import cProfile 
>>> from my_math import product 
>>> cProfile.run('product(1, 2)')
         4 function calls in 0.000 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 my_math.py:6(product)
        1    0.000    0.000    0.000    0.000 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

第二个参数向run提供一个文件名(如’my_math.profile’),分析结果将保存到这个文件中。然后,就可使用模块pstats来研究分析结果了。

>>> cProfile.run('product(1, 2)', 'my_math.profile')
>>> p = pstats.Stats('my_math.profile')
>>> p
<pstats.Stats object at 0x00000201869BACE0>

PS:

标准库还包含一个名为timeit的模块,提供了一种对小段Python代码的运行时间进行测试的简单方式。

小结

测试驱动编程:大致而言,测试驱动编程意味着先测试再编码。有了测试,你就能信心满满地修改代码,这让开发和维护工作更加灵活。

模块doctest和unittest:需要在Python中进行单元测试时,这些工具必不可少。模块doctest设计用于检查文档字符串中的示例,但也可轻松地使用它来设计测试套件。为让测试套件更灵活、结构化程度更高,框架unittest很有帮助。

PyChecker和PyLint:这两个工具查看源代码并指出潜在(和实际)的问题。它们检查代码的方方面面——从变量名太短到永远不会执行的代码段。你只需编写少量的代码,就可将它们加入测试套件,从而确保所有修改和重构都遵循了你采用的编码标准。

性能分析:如果你很在乎速度,并想对程序进行优化(仅当绝对必要时才这样做),应首先进行性能分析:使用模块profile或cProfile来找出代码中的瓶颈。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值