Rails rspec 实战篇

seaify · 2015年07月11日 · 最后由 torvaldsdb 回复于 2017年09月25日 · 5914 次阅读

本篇介绍 rspec 的使用

在未接触 rspec 之前,大量使用过 c, python, nodejs, php,但测试方面基本都是空白。现在想来,很多时候不是开发者不想写测试,而是并没有好的测试工具,不够灵活。写测试的难度很多时候都快等于开发了,甚至高过开发。

tdd 好处

先写出完备的测试,再去填坑,将失败的测试逐个通过,开发的过程变成了填坑通关的过程。

  1. 开发前,逼迫你全面的思考,思考将整个任务进行细分,细分到方便测试的程度,解耦成不同组件
  2. 更加心安,有安全感 (不知道你们是否有这种感受),方便功能调整,改变某部分功能时,对其它模块的影响都能迅速反应出来
  3. 方便新人了解系统

rspec 篇

rspec 时间相关测试篇

不少情况下,代码的运行逻辑和时间相关,如到了提前设定的时间范围内,执行 xxx 函数。这种情况下的测试,一般会将时间判断,单独抽离出来成为一个函数,对它进行测试,这时候,我们就需要 faker 时间了,不可能真等到设定的时间范围内。 使用timecop来 faker 时间,如下面例子,将当前的时间 Tim.now, 设定到 end_time 前 5s,以保证在时间范围内。

it "pass test now_is_in_order_flow_on_peroid?" do
  Timecop.freeze(@end_time - 5) do
    expect(Spree::Order.now_is_in_order_flow_on_peroid?).to eq true
  end
end

rspec 定时任务测试篇,如 whenever

代码中很可能使用 whenever 这样的 gem, 执行些定时任务,怎么测试呢? whenever 类似的 gem, 达到的效果是帮你生成相应的 crontab 任务,它做的事情其实是和我们的代码逻辑无关的。 我的理解是,如果是定时每隔 5 分钟,要执行下 model.xxx 函数,那实际上我们直接在 rspec 中调用 model.xxx 函数对其进行测试即可。 而如果要测试 whenever 的 crontab 语句生成是否正确,请参考https://github.com/javan/whenever/blob/master/test/unit/cron_test.rb , 大部分情况下,是没有必要对其进行测试的,如果是简单的每隔 xx 分钟/小时。

rspec helper 篇

  1. 在 web 的测试中,基本都会去检验是否登录,以及部分代码是登录后才能去执行,那在 model, controller 中,会有大量的测试,需要进行提前登录,这时候就需要使用 helper,在 support 中建立 login_helpers.rb,加入 login_user, login_admin, login_staff 这些函数,在需要做登录的测试中,引入 login_user 即可。

    def login_user
    before(:each) do
      user = FactoryGirl.create(:user)
      controller.send(:login_as, user)
    end
    end
    
  2. 因 rspec 测试并不会去读取 db/seeds 中的数据,但 model,controller 中的许多逻辑测试,可能依赖于某部分数据的提前建立,这时候可以在 support 中建立 data_helpers.rb, 加入 set_init_data 类似的函数,供所有测试去公用这段初始化数据的代码

rspec 数据伪造篇

许多情况下,需要伪造 email, name 这些类似的信息,可以使用 faker,随机出字符串和名字等等信息

FactoryGirl.define do
  sequence(:random_string)      { Faker::Lorem.sentence }
  sequence(:random_name)      { Faker::Name.name }
end

而如果要求是 uniq, 因为要确保 uniq, 变量的一部分就需要每次都不一样,这可以是 timestamp, 也可以是 n(0,1,2..),每次都不一样

sequence(:sku) { |n| "SKU-#{n}" }

时间比较匆忙,后面待续

我对测试的理解是, 1.用来分解问题 2.把问题具体化 3.将实现和重构分离 4.持续开发,就是说,不用担心把代码玩坏了。 5.测试简单来说,就是把 terminal 里面的东西“丢”到一个文件里。

Faker::Name.name 这个还是有机会出现重复的,不够 random。

TDD 是个很好的概念,但是同样也带来了问题。 比如说 TDD 本身推荐的「写 - 红-写 - 绿-重构」模型,实际开发过程中会严重影响思考效率。 反正我是宁愿先写一堆 pending usecase,然后开发完再填测试代码。

#3 楼 @msg7086 楼主说先写测试的 3 点好处,实际效率要比你先写代码高。实际上你可能还没正在去体验过先写测试再写代码,所以还是觉得先写测试会影响效率。先写测试用例,试试体验一段实际吧

#4 楼 @caiqinghua 我现在的确是先写测试用例啊。只是具体的代码会后填。 而且实际开发的时候经常会为怎么设计结构而烦恼,很难说可以一拍脑门就能写出很好的测试来。 所以我都是先写一堆 it 'xxxx yyy zzz',等代码写完了再回来根据测试用例和设计完的接口来写测试代码。

当然也可能是我水平太菜做不到先写测试……

你们没看去年 DHH 大骂 TDD 么?

测试没有定论,自己感觉好就行了。 自动测试是个好的发明,TDD 基本是个忽悠,忘掉这个词吧。

我目前是出了 bug,我就补写一个测试。只写了 model 和控制器测试。

我喜欢先用笔和纸,理清自己的思路后,再写测试。之后,如果逻辑简单,可能会做到一次性写完测试;如果逻辑复杂,那么就写一点测试,再写一点代码,在代码完成后再重构,一般不会重构测试代码。(从一位师兄那里学来的,虽然代码不幽雅,但解决了问题)。 最后表示被 rspec 坑过:rspec 大改语法,为了去掉那些恶心的 warning,改了整个项目的 rspec 语法。从此再也不爱 rspec 了。

#5 楼 @msg7086 不是水平问题,人有惯性

#8 楼 @caiqinghua 问题是先写测试代码我根本不知道怎么写啊。 实际开发完成前根本不知道最后接口、架构会设计成什么样。 想请教下你平时是怎么提前写测试代码的呢?

#2 楼 @ken

是这样的,random 是保证不了 uniq 的,已修正了这部分描述。

#6 楼 @chenge

出了 bug,是肯定要补测试的,我们也只测 model, controller, 而 view 这些测试不合适,变化比较快,成本高

#7 楼 @lance_zyb

我入 rails 不久,没经历过 rspec 的语法变化,O(∩_∩)O~,

一晃两年过去了,后面的待续部分呢?哈哈哈哈

需要 登录 后方可回复, 如果你还没有账号请 注册新账号