Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby

Ruby 新手训练营:用Rspec做测试

by
Difficulty:BeginnerLength:MediumLanguages:
This post is part of a series called Ruby for Newbies.
Ruby for Newbies: Missing Methods

Chinese (Simplified) (中文(简体)) translation by Duan Diana (you can also view the original English article)

Ruby是最流行的web应用语言之一 在Nettuts+上有我们发布的教程,向您讲解Ruby,以及与Ruby开发相关的实用框架和工具。 现在,您将学习到用Rspec测试Ruby 代码。Rspec是商用领域软件开发中最出色的测试库之一。


看视频讲解么?

似曾相识?

如果你已经看过我最近在JasmineJS上发布的教程,也许你会注意到他和Rspec的几分想通之处。 实际上,Jasmine是基于Rspec创建的 现在,我们一起来了解在Ruby中,如何使用Rspec进行TDD(TEST DRIVEN DEVELOPMENT)开发。 这个教程中,我们将生成一些设计好的Ruby classes,以此帮助我们熟悉Rspec 语法。 但是,接下来的“Ruby 新手训练营”教程中将介绍Rspec如何结合其他库来完成web应用程序的测试...不要着急,马上回来!


操练起来!

安装Rspec极其简单。 在命令行界面输入:

很简单吧。

现在,让我们建立一个小项目。 我们需要生成两个classes: Book以及Library. 我们的Book objects 用来存储 书名,作者,以及书的所属类别 我们的Library object用来存储一个关于书的表单,这个表单被保存成一个文件,允许我们通过类别来存取表单数据。

你的文件目录看起来应该是这个样子的:

Project Directory

我们将规范(或者简称 specs)存入一个叫 spec的文件夹,针对每一个class都会有一个规范(spec)文件 请留意这个 spec_helper.rb 文件。 为了要运行我们的规范(specs),需要 require 待测的 Ruby classes。 这就是为什么我们需要spec_helper文件

(看到require_relative眼熟么? 什么?不熟?好吧,require_relativerequire是一样一样一样滴啊,只不过文件搜索时并不针对你的Ruby 路径,而是当前目录的relative相关文件。)

也许你对YAML模块有点陌生;他是一个简单的文本数据库,用来存储数据的。 接下来我们会做详细介绍,你将了解到他是如何工作的。

准备好了吗?建立specs的征程开启了,让我们一起出发吧!


Book Class

让我们一起从Book class的测试开始启程。

征程始于 describe block. 我们的参数会向describe解释我们要测试的内容:参数可能是字符串,但在这个例子当中,我们使用 class 名。

那么在describe block中我们到底要输入些什么内容?

我们就从召唤before开始吧;我们将:each 传给规范文件,目的是在每个测试文件运行之前要先运行:each这段代码。(另外一种做法是运行所有测试之前运行一次:all). 那么我们在每次测试之前到底在做什么呢? 我们在生成一个Book 实例 留意一下,看我们如何生成一个实例变量,在变量名前加@就可以了 我们需要这么做的理由是为了我们的测试可以对这些变量进行访问。 否则,我们仅仅是生成了一个在before block有效的变量...这么做太不专业啦。

继续前进,

这是我们的第一个测试。 现在,用一个内嵌的describe block来说明,我们要描述一个规范方法里的种种行为。 你可能会主意到我使用了这个字符串“#new";这个Ruby 约定可以参考实例方法:ClassName#methodName. 因为我们在之前用到了类名 describe, 那就在这里引用这个方法名。

我们的测试简单讲,就是确定一定以及肯定我们生成了一个Book 实例。

主意一下语法:object.should do_something. 你所做测试的99%都需要用到这个格式:有一个测试实例,通过实例召唤should 或者should_not 然后,通过该实例再召唤另外一个功能。 在这个例子中,be_an_instance_ofBook将被视为单一参数)。 这样,一个可读性超赞的测试诞生了。 显而易见,@book是作为类Book的一个事件出现的。 好了,运行一下看看。

打开命令窗口,cd命令进入项目所在目录,然后运行 rspec spec. spec是一个文件夹,其中存放着rspec需要找到的所有测试。 你一定看到输出窗口显示着”uninitialized constant Object::Book"; 意思就是说这里类Book还不存在呢。 让我们来改改这个错误吧。

根据TDD的原则,我们的代码要精简到可以修复这个问题就好的程度。 在book.rb这个文件中,将会是这样的:

重新运行测试rspec spec, 这一次你会发现测试通过了。 我们没有初始化initialize方法,所以召唤Ruby#new现在不会看到有什么作用 但是,我们可以创建Book 实例(尽管是空的实例)。 通常的做法是,我们会将这个过程贯穿后续的开发过程:写一个测试(或者几个相关的测试),观察他们运行失败,修改后通过测试,重构,重复这个过程。 不过在这个教程中,我将演示的是测试和代码,然后根据这些来展开讨论。

接下来,为Book添加更多的测试吧:

这些内容通俗易懂。 但是这里有个小问题,在测试中我们要怎么作比较呢:eql 在Rspec里提供了三种方式用来判断是否相等:使用运算符==, 或者方法eql,他们的返回值为true时即两个实例拥有相同内容。 比如说,如何判断字符串和符号变量是否是一样的。 那么,当两个实例在内存中是真正相等的实例时,equal会返回true 在我们的例子中,eql(或者==)是我们需要用到的。

测试会失败,下面的代码会让Book通过测试:

现在一起来看一下Library!


Library 类是这样炼成的

这个类有点小难度。 先看一下这个:

精华都在这里啦:我们使用了两个before blocks: 一个给:each, 另外一个给:all. 在before:all block, 我们生成了books 数组。 接着我们打开文件"books.yml" (以“w" 可写模式)然后使用YAML将数组放入文件

简单来讲,YAML 就是,引用这句话来概况”对人类友好的适用于所有编程语言的数据系列化标准“ 类似于基于文本的数据库,比如JSON. 我们现在就要将YAML导入进spec_helper.rb YAML模块中有两个主要的方法:dump,他用来将序列化数据输出为字符串 接着,load 带着这个数据字符串并将它返回给Ruby objects.

这里,我们已经生成了一个带有数据的文件。 在:each测试之前,我们打算生成一个Library object, 并且将YAML 文件的文件名传给这个类。 现在让我们来看一下测试:

我们从一个内部的describe block开始,他是针对Library#new的方法。 我们在介绍一个新的block:context 允许我们为测试用例定义一个场景,或为不同的情况定义相应的输出结果。 在我们的例子中,我们提供两种不同的场景:”不带参数“ 以及”带yaml 文件参数“,在调用Library#new时会有两种不同的表现。

而且,注意我们在这两个测试用例中使用的验证方式:lib.should have(0).books 以及 @lib.should have(5).books. 另一种表达方式:lib.books.length.should == 5,但是这种方式的可读性较差。 然而,后一种方式表示了,我们需要有一个books 类其属性为数组,其中存放了所需要用到的书目。

接着,我们有三个测试案例用来验证以下功能,取得书籍所在目录,将一本书放入书库,保存书库。 运行失败,所以我们现在需要将给出相应的类。

我们可以为Library 类写更多测试用例,以及添加其他功能,不过让我们先回顾一下。 现在,运行 rspec spec, 你会看到所有测试用例运行通过。

上面显示的信息尽管并没有给出太多关于测试的结果。 但是,如果我们想要看到更详细的结果报告,要使用一个参数:rspec spec --format nested. 你将会看到这个:


下面还有几个验证要介绍:

结束之前,我还有几个验证要介绍一下:

  • obj.should be_true, obj.should be_false, obj.should be_nil, obj.should be_empty -前三个可以用布尔值 == true, 等,be_empty 的值可以为真 当 obj.empty? 结果为真。
  • obj.should exist -是判断这个实例是否存在?
  • obj.should have_at_most(n).itmes, object.should have_at_least(n).items - 像 have 一样,相应的,当待测满足多与或者少于 n 个目标值时测试则通过。
  • obj.should include(a[,b,...]) - 是判断是否一个数组中是否存在待测目标值。
  • obj.should match(sting_or_regex) - 判断是否待测满足字符串或者regex?
  • obj.should raise_exception(error) - 判断当这个方法被调用的时候是否有异常?
  • obj.should respond_to(method_name) - 判断某个实例是否有对应的方法? 可以取得多个方法名。

想要了解更多相关知识么?

Rspec 是Ruby世界里最出色的测试框架之一,有兴趣的话,你将了解到更多关于他的强大之处。 想要了解更多知识,就去the Rspec website看一看吧。 有一本书 The Rspec Book 也值得一读,他会介绍很多和Rspec相关的知识,从中,你会了解到关于Ruby世界里 TDD 和BDD 的那些事儿。 我现在就在读,这本书的内容详尽到位,值得一读。

好啦,到这里这节课就讲完了! 下一次,我们会介绍如何使用Rspec做web 应用的接口测试。

关注我们的公众号
Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.