测试驱动开发基础知识的实例介绍

82 阅读3分钟

测试驱动开发(TDD)是程序员用来产生更高质量代码的一种方法。编码的 "典型 "程序一直是代码第一,测试第二。TDD通过在实际编码之前专注于测试来改变这种思维方式。这篇文章是对基本原理的简要概述,并举了一个小例子说明它是如何工作的。我们将使用Python,更确切地说,是标准Python库中提供的unittest 框架。虽然Python是这个例子中使用的语言,但TDD的过程与任何语言都无关。

过程

Step-by-step process of TDD

这张图显示了TDD的高级步骤。我们的目标是尽可能做最少的工作以使测试通过,并避免使过程过于复杂。使用一个例子,我们将通过每个步骤来展示它是如何工作的。如果你对Python的unittest 框架不熟悉,你可以查看这个教程以了解总体情况(或者你应该能够从代码的注释中获得很多功能)。

例子概述

我们想创建一个脚本来格式化街道地址列表。我们的主要要求是确保任何地址中没有小数。

第一步:编写测试

这第一个测试将检查我们的地址是否被成功加载到列表中。

# File: "test.py"

import unittest # Import the unittest framework
from my_code_block import format_addresses # Import the function from our script file

# Create our own test case class that inherits from the base TestCase class
class AddressTestCase(unittest.TestCase):

  # Do an intial setup to make the addresses available to all tests
  def setUp(self):
    self.addresses = ['634 Tomato Way', '233 E. 500 S.', '1800 N. Python Lane']

  def test_address_in_list(self):
      # This assert checks that the first address is in the list by seeing if it's equal
      first_address = format_addresses(self.addresses)[0]
      self.assertEqual(first_address, '634 Tomato Way')

第2步:运行和失败测试

结果是测试失败,这是预料之中的,因为我们还没有写任何东西。

 $ python -m unittest
======================================================================
ERROR: test_address_in_list (__main__.AddressTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File 'test.py', line 12, in test_address_in_list
    first_address = format_addresses(self.addresses)[0]
NameError: name 'format_addresses' is not defined

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

第3步:编写代码

现在我们可以实际写代码,使其成功。

# File: "my_code_block.py"

def format_addresses(addresses):
  return addresses

第4步:运行并通过测试

 $ python -m unittest
----------------------------------------------------------------------

Ran 1 test in 0.000s

OK

第5步:重构代码

在这一点上,没有太多的代码需要重构,因为这是一个非常随意的例子,但是在做完每一轮之后,花点时间看看有哪些代码可以合并,使之更有效率。

现在重复!

我们现在将应用同样的原则来格式化地址,使其不含任何小数。

第1步:编写测试

# File: "test.py"

import unittest
from my_code_block import format_addresses

class AddressTestCase(unittest.TestCase):

  # Do an intial setup to make the addresses available to all tests
  def setUp(self):
    self.addresses = ['634 Tomato Way', '233 E. 500 S.', '1800 N. Python Lane']

  def test_address_in_list(self):
      first_address = format_addresses(self.addresses)[0]
      self.assertEqual(first_address, '634 Tomato Way')

  # ***************** Added decimal test ********************
  def test_decimal(self):
      no_decimals = True

      formatted_addresses = format_addresses(self.addresses)
      for value in formatted_addresses:
          if '.' in value:
              no_decimals = False

      # Run an assert on whether or not there were decimals in one of the addresses.
      self.assertTrue(no_decimals)

第2步:运行和失败测试

 $ python -m unittest
======================================================================
FAIL: test_decimal (__main__.AddressTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File 'test.py', line 23, in test_decimal
    self.assertTrue(no_decimals)
AssertionError: False is not true

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

FAILED (failures=1)

第3步:编写代码

# File: "my_code_block.py"

def format_addresses(addresses):
  formatted_addresses = []

  for value in addresses:
    value = value.replace('.', '')
    formatted_addresses.append(value)

  return formatted_addresses

第4步:运行并通过测试

 $ python -m unittest
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

第5步:重构代码

然后再仔细检查如何改进你的代码。

重复!

对我来说,这是很难理解的。它似乎与我的天性相悖,即先测试。但是一旦我使用这种新的模式做了几个项目,我觉得我的代码变得更干净了。TDD允许你在问题存在之前就将其解决。