《零基础学Python》敏捷方法学在Python中的应用——测试驱动开发【十六】

整体文章目录

整体目录

一、 当前章节目录

在这里插入图片描述

二、测试驱动开发

测试驱动开发是敏捷开发中的一个重要组成部分,其基本思想是测试先行。也就是说,在开发具体的功能代码之前,需要首先编写此功能的测试代码。只有通过了测试代码,才能够加入代码仓库中。

2.1 测试驱动开发模式

传统的软件开发模型包括瀑布模型、演进模型、螺旋模型、喷泉模型和智能模型等。
在传统的软件开发模型中,测试阶段是放在软件生命周期的后期来完成的。这种处理方式使得软件编码中的问题在后期才能够被发现,从而会造成严重的后果。在这种开发模式下,测试阶段所占的时间比例要占整个开发所用时间的一半左右。为了避免传统开发模式下的这些问题,敏捷方法学在快速开发的目标上强调基于测试的开发过程。
在这里插入图片描述
在这里插入图片描述

2.2 TDD的优势

因为TDD采用的是以测试作为核心的开发模式,所以具有传统开发模型所不具备的优点。

  1. 可以在短时间内构建出一个软件的原型。
  2. 改正程序错误方面。
  3. 项目主管可以很清楚地看到已经完成了哪些软件需求,从而有助于把握项目进度。
  4. 通过编写测试用例,优先考虑实现代码的使用需求,包括功能、过程和接口等。能够提供代码的内聚性和复用性。

2.3 TDD的使用步骤

  • 明确当前代码需要完成的功能,必要的时候书写相关的接口等。
  • 快速新增一个测试。
  • 运行所有的测试,看是否可以通过。若是通过,跳到步骤6。
  • 对功能代码进行细微的改动。
  • 重新运行所以的测试用例,保证全部通过。
  • 对代码进行重构,消除重复设计,优化代码结构。

两类代码测试代码模块:

  • unittest:可以用来书写PyUnit的测试代码,和其他语言的单元测试工具一样,支持对软件代码的自动化测试。
  • docttest:是一个特殊的测试模块,此模块将测试用例内置在了函数的文档字符串中,从而实现了文档和测试代码的统一。

三、unittest框架

3.1 unittest模块介绍

  • 支持测试的自动化处理。
  • 支持单元测试的各种重要概念。其中最重要的概念是测试用例。
  • 一组测试用例包含共同需要处理的代码,称之为测试固件。这些代码可能是测试之前需要进行的初始化工作,也可能是测试结束后所要做的代码清理工作。
  • 随着测试用例的增多,逐个管理测试用例显然是非常低效的。unittest模块提供了测试套件来解决这个问题。测试套件将一组测试用例集合起来作为一个测试对象。
  • 包含一个测试运行器,用来运行这些测试并为用户提供输出。

3.2 构建测试用例

import unittest     # 导入模块
class StringReplaceTestCase1(unittest.TestCase):
    """测试空字符替换"""
    def runTest(self):
        source = "HELLO"
        expect = "HELLO"
        result = source.replace(source, "")
        self.assertEqual(expect, result)

class StringReplaceTestCase2(unittest.TestCase):
    """测试空字符替换成常规字符"""
    def runTest(self):
        source = "HELLO"
        expect = "*H*E*L*L*O*"
        result = source.replace("", "*")
        self.assertEqual(expect, result)

class StringReplaceTestCase3(unittest.TestCase):
    """测试常规字符替换为空字符"""
    def runTest(self):
        source = "HELLO"
        expect = "HEO"
        result = source.replace("LL", "")
        self.assertEqual(expect, result)

class StringReplaceTestCase4(unittest.TestCase):
    """测试常规字符替换"""
    def runTest(self):
        source = "HELLO"
        expect = "HEMMO"
        result = source.replace("LL", "MM")
        self.assertEqual(expect, result)

在这里插入图片描述

3.3 构建测试固件

import unittest     # 导入模块

class SimpleStringReplaceTestCase(unittest.TestCase):
    """准备测试的源字符串"""
    def setUp(self):
        self.source = "HELLO"

class StringReplaceTestCase1(SimpleStringReplaceTestCase):
    """测试空字符替换"""
    def runTest(self):
        expect = "HELLO"
        result = self.source.replace(source, "")
        self.assertEqual(expect, result)

class StringReplaceTestCase2(SimpleStringReplaceTestCase):
    """测试空字符替换成常规字符"""
    def runTest(self):
        expect = "*H*E*L*L*O*"
        result = self.source.replace("", "*")
        self.assertEqual(expect, result)

class StringReplaceTestCase3(SimpleStringReplaceTestCase):
    """测试常规字符替换为空字符"""
    def runTest(self):
        expect = "HEO"
        result = self.source.replace("LL", "")
        self.assertEqual(expect, result)

class StringReplaceTestCase4(SimpleStringReplaceTestCase):
    """测试常规字符替换"""
    def runTest(self):
        expect = "HEMMO"
        result = self.source.replace("LL", "MM")
        self.assertEqual(expect, result)

3.4 组织多个测试用例

import unittest     # 导入模块

class StringReplaceTestCase(unittest.TestCase):
    """准备测试的源字符串"""
    def setUp(self):
        self.source = "HELLO"

    """测试空字符替换"""
    def testBlank(self):
        expect = "HELLO"
        result = self.source.replace(source, "")
        self.assertEqual(expect, result)

    """测试空字符替换成常规字符"""
    def testBlankOrd(self):
        expect = "*H*E*L*L*O*"
        result = self.source.replace("", "*")
        self.assertEqual(expect, result)

    """测试常规字符替换为空字符"""
    def testOrdBlank(self):
        expect = "HEO"
        result = self.source.replace("LL", "")
        self.assertEqual(expect, result)

    """测试常规字符替换"""
    def testOrd(self):
        expect = "HEMMO"
        result = self.source.replace("LL", "MM")
        self.assertEqual(expect, result)

3.5 构建测试套件

def suite():
    StringReplaceTestSuite = unittest.TestSuite()

    StringReplaceTestSuite.addTest(SimpleStringReplaceTestCase("testBlank"))
    StringReplaceTestSuite.addTest(SimpleStringReplaceTestCase("testOrd"))

    return StringReplaceTestSuite
def suite():
    tests = ['testBlank', 'testOrd']
    StringReplaceTestSuite = unittest.TestSuite(
        map(StringReplaceTestCase, tests)
    )
    return StringReplaceTestSuite
StringReplaceTestSuite = unittest.makeSuite(StringReplaceTestCase, 'test')
    return StringReplaceTestSuite
import unittest     # 导入模块

class StringStripTestCase(unittest.TestCase):
    def testBlank(self):
        expect = "HELLO"
        st = "HELLO     "
        result = st.strip()
        self.assertEqual(expect, result)

    def testStr(self):
        expect = "HELLO"
        st = "xxHELLOxx"
        result = st.strip("xx")
        self.assertEqual(expect, result)

class StringReplaceTestCase(unittest.TestCase):
    # shenglu

def suite():
    StringStripTestSuite = unittest.makeSuite(StringStripTestCase, 'test')
    StringReplaceTestSuite = unittest.makeSuite(StringReplaceTestCase, 'test')

    alltests = unittest.TestSuite((StringStripTestSuite, StringReplaceTestSuite))
    return alltests

3.6 重构代码

import unittest     # 导入模块

class StringReplaceTestCase(unittest.TestCase):
    """准备测试的源字符串"""
    def setUp(self):
        self.source = "HELLO"

    def checkequal(self, result, object, methodname, *args):
        realresult = getattr(object, methodname)(*args)
        self.assertEqual(
            result,
            realresult
        )

    """测试空字符替换"""
    def testBlank(self):
        self.checkequal("HELLO", self.source, "replace", "", "")

    """测试空字符替换成常规字符"""
    def testBlankOrd(self):
        self.checkequal("*H*E*L*L*O*", self.source, "replace", "", "*")

    """测试常规字符替换为空字符"""
    def testOrdBlank(self):
        self.checkequal("HE*O", self.source, "replace", "LL", "*")

    """测试常规字符替换"""
    def testOrd(self):
        self.checkequal("HEMMO", self.source, "replace", "LL", "MM")

3.7 执行测试

import unittest     # 导入模块

class StringStripTestCase(unittest.TestCase):
# 省略StringStripTestCase实现代码

class StringReplaceTestCase(unittest.TestCase):
# 省略StringReplaceTestCase实现代码

def suite():
    StringStripTestSuite = unittest.makeSuite(StringStripTestCase, 'test')
    StringReplaceTestSuite = unittest.makeSuite(StringReplaceTestCase, 'test')

    alltests = unittest.TestSuite((StringStripTestSuite, StringReplaceTestSuite))
    return alltests

if __name__ == "__main__":
    runner == unittest.TextTestRunner()

    runner.run(suite())

代码
In [1]: import unittest

In [2]: import utest6

In [3]: runner = unittest.TextTestRunner()

In [4]: runner.run(utest6.StringReplaceTestCase("testBlank"))
.
----------------------------------------------------------------------
Ran 1 test in 0.004s

OK
Out[4]: <unittest.runner.TextTestResult run=1 errors=0 failures=0>
# 省略部分代码

def suite():
    StringStripTestSuite = unittest.makeSuite(StringStripTestCase, 'test')
    StringReplaceTestSuite = unittest.makeSuite(StringReplaceTestCase, 'test')

    alltests = unittest.TestSuite((StringStripTestSuite, StringReplaceTestSuite))
    return alltests

if __name__ == "__main__":
    # runner == unittest.TextTestRunner()
    # runner.run(suite())

    unittest.main()
In [5]: %run utest7.py
......
----------------------------------------------------------------------
Ran 6 tests in 0.007s

OK

In [6]: %run utest7.py -v
testBlank (__main__.StringReplaceTestCase) ... ok
testBlankOrd (__main__.StringReplaceTestCase) ... ok
testOrd (__main__.StringReplaceTestCase) ... ok
testOrdBlank (__main__.StringReplaceTestCase) ... ok
testBlank (__main__.StringStripTestCase) ... ok
testStr (__main__.StringStripTestCase) ... ok

----------------------------------------------------------------------
Ran 6 tests in 0.009s

OK

In [7]: %run utest7.py -v
testBlank (__main__.StringReplaceTestCase) ... ok
testBlankOrd (__main__.StringReplaceTestCase) ... ok
testOrd (__main__.StringReplaceTestCase) ... ok
testOrdBlank (__main__.StringReplaceTestCase) ... FAIL
testBlank (__main__.StringStripTestCase) ... ok
testStr (__main__.StringStripTestCase) ... ok

======================================================================
FAIL: testOrdBlank (__main__.StringReplaceTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\86155\Desktop\Python\第16章 敏捷方法学在Python中的应用——测试驱动开发\utest7.py", line 38, in testOrdBlank
    self.checkequal("HEO", self.source, "replace", "LL", "*")
  File "C:\Users\86155\Desktop\Python\第16章 敏捷方法学在Python中的应用——测试驱动开发\utest7.py", line 23, in checkequal
    self.assertEqual(
AssertionError: 'HEO' != 'HE*O'
- HEO
+ HE*O
?   +


----------------------------------------------------------------------
Ran 6 tests in 0.013s

FAILED (failures=1)
An exception has occurred, use %tb to see the full traceback.

SystemExit: True

四、使用doctest进行测试

4.1 doctest模块介绍

doctest模块作为一种新的单元测试框架,可以有效地利用代码注释中的文档内容。这种处理方式使得文档即可以作为测试代码来执行。

4.2 构建可执行文档

def factorial(n):
    """
    这里包含了多个测试例,用来测试factorial方法的返回值
    >>> [factorial(n) for n in range(6)]
    [1, 1, 2, 6, 24, 120]
    >>> factorial(30)
    265252859812191058636308480000000
    >>> factorial(-1)
    Traceback (most recent call last):
        ...
    ValueError: n must be >= 0

    Factorials of floats are OK, but the float must be an exact integer:

    检测浮点数是否为整数
    >>> factorial(30.1)
    Traceback (most recent call last):
        ...
    ValueError: n must be exact integer
    >>> factorial(30.0)
    265252859812191058636308480000000

    It must also not be ridiculously large:
    溢出检查
    >>> factorial(1e100)
    Traceback (most recent call last):
        ...
    OverflowError: n too large
    """

    import math
    if not n >= 0:
        raise ValueError("n must be >= 0")
    if math.floor(n) != n:
        raise ValueError("n must be exact integer")
    if n+1 == n:    # catch a value like le300
        raise OverflowError("n too large")
    result = 1
    factor = 2
    while factor <= n:
        result *= factor
        factor += 1
    return result

def _test():
    import doctest
    doctest.testmod()
    # doctest.testfile("factorial_docstring.txt")

if __name__ == "__main__":
    _test()

# if __name__=='__main__':
#     import doctest
#     doctest.testmod(verbose=True)

4.3 执行doctest测试

In [1]: %run dtest8.py

In [2]: %run dtest8.py -v
Trying:
    [factorial(n) for n in range(6)]
Expecting:
    [1, 1, 2, 6, 24, 120]
ok
Trying:
    factorial(30)
Expecting:
    265252859812191058636308480000000
ok
Trying:
    factorial(-1)
Expecting:
    Traceback (most recent call last):
        ...
    ValueError: n must be >= 0
ok
Trying:
    factorial(30.1)
Expecting:
    Traceback (most recent call last):
        ...
    ValueError: n must be exact integer
ok
Trying:
    factorial(30.0)
Expecting:
    265252859812191058636308480000000
ok
Trying:
    factorial(1e100)
Expecting:
    Traceback (most recent call last):
        ...
    OverflowError: n too large
ok
2 items had no tests:
    __main__
    __main__._test
1 items passed all tests:
   6 tests in __main__.factorial
6 tests in 3 items.
6 passed and 0 failed.
Test passed.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值