Day 5 TDD 测试驱动开发

该文章同步发布於:我的部落格

什麽是 TDD (Test-Driven Development) ?

是一种有别於传统的开发方式,这边说的是 开发方式 而不是测试方式喔!

这时候一定会想说,奇怪?我们主轴不是写测试吗?怎麽突然提到开发方式,是因为 RSpec 本身就是遵循的 TDD 的理念和精神而开发的,也是 RSpec 的核心价值。

所以对於 TDD 我们是不得不提,要来歌颂一下他的好处!

最直白的说明这个开发方式的流程就是:

  1. 先写测试代码
  2. 试着让它通过测试
  3. 解决重复以及优化程序码!

直接上图片,这样更容易理解整个脉络:

这就是 TDD 测试的核心,经由这三个步骤形成一个无限回圈,也确保程序码的品质能够在这个回圈中精进,简化!

先写测试代码

从第一个步骤开始细讲,要如何先开始写测试代码呢?

我们在工作上,会遇到客户提出的需求以及功能,这时候我们就可以依照客人想要的功能来撰写测试代码!

我们主要的思考是要如何把需要做的事情写成测试代码,意思就是,我们让这些测试代码通过,就等於完成客户的需求 ( 广义 )。

当然这测试码必须要写得非常的详细,考虑周全,站在使用者的角度,想想什麽东西应该要有,什麽其实不必要也不在需求内。

所以才会听到有人说,写测试是在写规格 的原因就是这样。

因为完成一段测试代码的时候,等於是把所有的因素考虑进去,也就是一个功能的规格,他会有固定的 Output 有限制的 Input 也有例外地捕捉等等...

以上都属於第一步骤,这边的测试全部都不会通过,只是凭空想像,发挥你的想像力,让物件像真实的世界一样动起来吧!

以下进入 TDD 的情境,客户来了一个需求 ( 只是假设,不会有这麽好笑的需求 )

  1. 我想要我的网站可以开商店
  2. 商店要有名字
  3. 商店能够卖商品

接着我们撰写关於这些需求的测试码:

RSpec.describe Store do
  it 'have name' do
    store = Store.new 'Happy Store'
    expect(store.name).to eq 'Happy Store'
  end
  
  it 'sale product' do
    store = Store.new 'Happy Store'
    expect(store.sale).to eq 'Earn lost of money'
  end
end

接着在终端机输入 rspec .

rspec 这个指令是动词,意味着执行
. 这个受词则是所有的 _spec.rb 档案
所以你其实可以 rspec ..._spec.rb or rspec spec/..._spec.rb

测试结果:

我们写了一段关於客户需求的测试码,想当然一定不可能会通过。

因为我们还没有写真正的程序码,但关於这个测试码,破烂英文如我,也都大概看得懂,虽然不清楚详细的语法,但猜一下也不难,就在於我们没有一个 Store 的类别。

回头思考一下,我们建立了一个商店,然後测试他的名字,以及他卖东西的动作,根据需求来撰写,这是之後会一直强调的地方!

试着让它通过测试

刚刚看到一片满江红,应该也不觉得是什麽好事,现在我们需要开始撰写真实的程序码来通过这一个测试。

接着我们撰写程序码来让它通过( 你可以先把它写在测试的上方,之後我们会用 require 的方式 ):

class Store
  def initialize(name)
    @name = name
  end
  
  def name
    @name
  end
  
  def sale
    'Earn lots of money'
  end
end

测试结果:

成功!

我们撰写一个类别,然後可以读取他的名字,也有贩卖的动作。

测试代码也成功地通过了,那接下来我们就要进入到第三个阶段,重构的部分。

优化程序码

接着我们回头看一下程序码,看看有哪里需要修正的,或是重复的测试代码也可以,试着让我们交出去的程序码是乾净且易读的。

我就想到了,刚刚 Store 类别中的 name 方法,记得有个像是 attr_reader 的方法可以使用,这样就可以三行变一行,快来试试看!

class Store
  attr_reader :name
  
  def initialize(name)
    @name = name
  end
  
  def sale
    'Earn lots of money'
  end
end

还有测试代码中 Store.new 也重复的写了两次,我们来让他变得更精简吧!

Rspec.describe Store do
  let(:store) { Store.new('Happy Store') }
  
  it 'have name' do
    expect(store.name).to eq 'Happy Store'
  end
  
  it 'sale product' do
    expect(store.sale).to eq 'Earn lots of money'
  end
end

我们把重复的地方利用方法整理了一下,然後也把 name 方法用 Ruby 内建的小魔法处理了一下!

!!!记住!!!

重构结束之後也记得要在测试一次喔!

这时候写测试的好处就来了,当你在重构代码的时候,只要轻轻一按,就能知道刚刚的修正有没有什麽错误的产生,我们快来试试看吧!

测试结果:

依然通过测试,代表这一个 TDD 回圈就暂时告一个段落,而且也依照客户的需求完成了功能。

以上是 TDD 测试的流程,虽然范例蛮烂的,但碍於这篇只是想介绍 TDD 的价值和精神,所以就随便的举例,希望能够让你们看懂!

小结

明天开始,我们就会进入 RSpec 的语法介绍篇!

我会从基本的方法介绍起来,也会搭配范例服用,那就明天见啦!


<<:  Day 05 - 了解FOREIGN KEY 外键限制!

>>:  Day04 UIKit 03 - SceneDelegate

Day-28 特集:例外处理与FP

本篇介绍两种错误检测函式。 Error Handling例外处理 例外处理 (error handl...

[ Day 03 ] 建立一个 React.js 专案

对 React.js 有基本的概念後,我们就可以继续往下学习该如何建置和使用它罗! 开始环境建置 ...

Day4 第一个HTML网页制作

VS CODE安装好之後,就可以来认识HTML啦~ 开始写HTML前的步骤 首先,在桌面上新增一个资...

Day 29 SQLite资料库

(一)介绍 当我们需要对资料进行大量递增、删、改、查操作时,我们就会使用到资料库,而最广泛被应用的就...

【Day 01】- 前言: 从 0 开始的网路爬虫

前言 近年人工智慧与大数据十分热门,其背後需要许多有效的资料,先不论 Data Tagging 的部...