对Ruby on Rails进行高效的单元测试的教程

2019-09-25 09:43:43刘景俊

Factory的使用

有了这个工厂,可以很方便的构造不同的模拟数据来运行测试代码。还是上面的例子,如果要测试absence_at方法,涉及到多个model:

    HistoryRecord:User的上课记录     Calendar:User的课程表     Logging:User的日志信息

如果不用factory-girl构造测试数据,我们将不得不在fixture构造这些测试数据。在fixture构造的数据无法指定是那个测试用例使用,但是如果用Factory的话,可以为这个方法专门指定一组测试数据。

Factory.define :user_absence_example,:class => User do |user|
 user.login "test"
 class << user
  def default_history_records
   [Factory.build(:history_record,:started_at=>Time.now),
    Factory.build(:history_record,:started_at=>Time.now)]
  end
  def default_calendars
   [Factory.build(:calendar),
    Factory.build(:calendar)]      
   end
   def default_loggings
   [Factory.build(:logging,:started_at=>1.days.ago),
    Factory.build(:logging,:started_at=>1.days.ago)]
   end
  end
  user.history_records {default_history_records}
  user.calendars {default_calendars}
  user.loggings {default_loggings}
end

这个测试数据的构造工厂,可以放在factories.rb文件中,方便其他测试用例使用,也可以直接放到测试文件的before中,仅供本测试文件使用。通过factory的构造,不仅可以为多个测试用例共享同一组测试数据,而且测试代码也简洁明了。

before :each do
 @user = Factory.create(:user_absence_example)
end

Readonly的测试

在笔者的系统中,大量使用了acts_as_readonly,从另外一个数据库来读取数据。由于这些model并不在本系统中,所以当用Factory构造测试数据的时候,总会有问题。虽然也可以使用mock来达到这个目的,但是由于mock的局限性,还是无法灵活的满足构造测试数据的需要。为此,扩展了一些代码,使得这些model依然可以测试。核心思想则是,根据配置文件的设置,将对应的readonly的表创建在测试数据库,这个操作在运行测试之前执行,这样就达到与其他model一样的效果。site_config配置文件中,关于readonly的配置格式如下:

readonly_for_test:
 logings:
  datetime: created_at
  string: status
  integer: trainer_id

Gem的测试

Gem在Rails中被广泛使用,而且是最基础的东西,因此它的准确无误就显得更加重要。在不断实践的基础上,笔者所在的团队总结出一种用spec测试gem的方法。假设我们要测试的gem是platform_base,步骤如下:

1. 在gem的根目录下创建一个目录spec(路径为platform_base/spec)。

2. 在gem的根目录下创建文件Rakefile(路径为platform_base/Rakefile),内容如下:

require 'rubygems'
require 'rake'

require 'spec/rake/spectask'

Spec::Rake::SpecTask.new('spec') do |t|
 t.spec_opts = ['--options', "spec/spec.opts"]
 t.spec_files = FileList['spec/**/*_spec.rb']
end