总文档连接:

RSpec.info/documentation/

包括core, expectiation,rails , mock, 点击最新版本,然后右上角搜索class, method.

第3章:

rspec-expectations gem: RSpec匹配器

第4章:

预构件gem:  factory_bot

git checkout -b my-02-setup origin/01-untested

解释:从已经克隆后的远程,下载分支到本地。

Branch my-02-setup set up to track remote branch 01-untested from origin.

Switched to a new branch 'my-02-setup'


第二章 开始

1.

Gemfile group :development, :test do

gem 'rspec-rails', '~> 3.6.0' # 省略了 Rails 提供的其他 gem

end

2. 测试数据库。

3. 配置RSpec:

$ bin/rails generate rspec:install

打开.rspec文件,增加:

--format documentation   #文档阅读模式,在测试时显示要测试的文本,并标记是否成功。
--warnings   #添加警告标志,在真实应用中有用。

作者强烈建议看spec/spec_helper.rb和spec/rails_helper.rb的设置,理解各项设置的作用。

3.1 使用 rspec binstub 提升测试组件的启动速度

Gemfile

group :development do

# 这一分组中的其他 gem ...

gem 'spring-commands-rspec'

end

然后 bundle exec spring binstub rspec, 如果不想用这个,直接bundle exec rspec.

然后bin/rspec试一试。

4 controller(可选)

可以使用 rspec-rails 提供的默认设置;不过这样会生成额外的样板代码,你可以自己动手删 除,也可以放着不管。

打开 config/application.rb 文件,在 Application 类中加入下面的代码:

module Projects
  class Application < Rails::Application
    config.load_defaults 5.1
    config.generators do |g|
      g.test_framework :rspec,
        fixtures: false  #暂时不要固件
        view_specs: false #不要视图测试,UI相关的测试交给集成测试。
        helper_specs: false #生成控制器时,不生成对应的辅助方法测试文件
        routing_specs: false #是不生成针对 config/routes.rb 的测试文件。
        #如果是大型应用,路由很复杂,最好还是测试一下
    end
  end
end

5,附加:(Rails指南有一整章节。)

如果经常新建Rails, 可以创建一个Rails 应用模板,自动把 RSpec 和相关的配置添加到应用 的 Gemfile 和配置文件中,而且还可以自动创建测试数据库。Daniel Kehoe 开发的 Rails Composer 是个 不错的工具。


第 3 章 模型测试

git checkout -b my-03-models origin/02-setup

复制前一章分支,增加了user, project, note, task.4个models. 添加了5个gem。

1.2编写模型测试。

bin/rails g rspec:model user

模型测试应该包含:

• 使用有效属性实例化的模型应该是有效的;

• 无法通过数据验证的数据,测试应该失败;

• 类方法和实例方法应按预期可正常使用。

2.2编写模型测试 

bin/rspec

首先,确定要验证的example。模型验证User

RSpec.describe User, type: :model do
  it "is valid with a first name, last name, email, and password"
  it "is invalid without a first name"
  it "is invalid without a last name"
  it "is invalid without an email address"
  it "is invalid with a duplicate email address"
  it "returns a user's full name as a string"
  # pending "add some examples to (or delete) #{__FILE__}"
end

pending在之后章节学习。

2.3 RSpec句法:新:expect().to eq()

  it "is valid with a first name, last name, email, and password" do
    user = User.new(
      first_name: "Aaron",
...
    )
    expect(user).to be_valid   #be_valid也是匹配器。
  end

然后bin/rspec,通过验证。

2.4 测试数据验证

include匹配器检验一个可以枚举的value中是否包括指定的值。  to_not和to匹配器。

  it "is invalid without a first name" do
    user = User.new(first_name: nil)
    user.valid?  #Rails语法,看是否新建的实例通过全部验证validations
    expect(user.errors[:first_name]).to include("can't be blank")
  end

然后bin/rspec通过验证。

另外,可以修改应用代码,看看对测试有什么影响。如果测试代码的输出没有变化,可能是测试没有与代码对接上,或者代码的行为与预期不同。

关联模型的数据验证:

bin/rails g rspec:model project

这会生成models/project_spec.rb文件。也可以手动添加,但通过控制器添加文件,能防止打错字。

加2个example.

it "does not allow duplicate project names per user"

it "allows two users to share a project name"

因为在app/models/project.rb中,增加了作用域验证,这里每个project的name只有一个用户,name不是唯一的但它对应的user_id,在Project中是唯一的。

validates :name, presence: true, uniqueness: { scope: :user_id }

我们应当养成数据验证的习惯,通过和不通过都要验证。

比如:把验证代码临时注释掉,或者把测试的预期改成其他值,看看测试结果是否失败?

3.5 测试实例方法

在User model中编写一个实例方法name。可以在测试文件user_spec.rb中使用,例子:

expect(user.name).to eq("Dave Bot")

3.6测试类方法和作用域

在Note model中编写一个类方法search,它是一个作用域 :

  scope :search, ->(term) {
    where("LOWER(message) LIKE ?", "%#{term.downcase}%")
  } 
  • #A scope是狭义的data 查询。用于检索和查询类的实例对象
  • #scope()是类方法:scope(name, body, &body), 等同于:
  def self.search(term)
    where("LOWER(message) LIKE ?", "%#{term.downcase}%")
  end
  • #返回的是一个相关的记录数据集合对象,ActiveRecord::Relation,类似Array
  • #箭头函数是lambda表达式,->(){return},这里成为块参数。
  #where("LOWER(message) LIKE ?", "%#{term.downcase}%"):

WHERE .. LIKE.. 模糊对比:SELECT * FROM events WHERE name LIKE '%Ruby%';

这里where()是rails语法糖。返回一个relation对象。

具体看query guide. 或者自己的博客activerecord:query

project = user.projects.create(name: "Test project")
note1 = project.notes.create(
  message: "This is the first note",
  user: user,
)

expect(Note.search("first")).to include(note1)

3.7 测试失败情况

使用be_empty匹配器,检查Note.search("something")的返回值是否为空。

expect(Note.search("message")).to be_empty

空的话:expected `#<ActiveRecord::Relation []>.empty?` to return true.

3.8 匹配器

be_valid, eq, include , be_empty。 be_valid由rspec-rails gem提供。

其他的由一起安装的rspec-expectations gem提供 :https://github.com/rspec/rspec-expectations

第8章 ,自定义匹配器

3.9 refactor重构: describe, context, before, after.

describe 用来表示 需要实现的功能

context 针对该功能不同的情况

it : 返回什么结果。 使用动词说明希望得到的结果。

before: 设定测试数据。消除重复。这是简写,全的是before(:each),在块内的每个example之前运行一次,有几个example就运行几次。

before(:all):在块内的all examples之前运行一次,之后不再运行。

before(:suit):) 在整个测试组件之前运行。

after: 假如测试在用例执行后需要做些清理工作,例如中断与外部服务的连接, after 块也有 each、all 和 suite 三个选项。因为 RSpec 会自行清理数据库,所以我很 少使用 after。

3.10小结:

  • 明确指定希望得到的结果:使用动词说明希望得到的结果。每个测试用例只测试一种情况。
  • 测试希望看到的结果和不希望看到的结果
  • 测试极端情况
  • • 测试要良好地组织,保证可读性:使用 describe 和 context 组织测试,形成一个清晰的大纲;使用 before 和 after 块消除代码重复。

第 4 章 创建有意义的测试数据

用预构件代替固件fixture

4.1安装

Gemfile

group :development, :test do
gem "rspec-rails", "~> 3.6.0"
gem "factory_bot_rails" # 改名了,以前叫factory_girl

end

config/application.rb 把fixture: false去掉了。

4.2 开始用
bin/rails generate factory_bot:model user
create spec/factories/users.rb
FactoryBot.define do
factory :user do first_name "Aaron" last_name "Sumner" email "tester@example.com" password "dottle-nouveau-pavilion-tights-furze" end # 现在,在测试中可以调用 FactoryBot.create(:user) end

spec/models/user_spec.rb

  require 'rails_helper'

describe User do
it "has a valid factory" do

expect(FactoryBot.build(:user)).to be_valid

end

## 其他测试用例 ...

    it "is invalid without an email address" do
      # user = User.new(email: nil)
      user = FactoryBot.build(:user, email:nil)
      user.valid?
      expect(user.errors[:email]).to include("can't be blank")
    end
    it "is invalid with a duplicate email address" do
      FactoryBot.create(:user, email:"arron@example.com")
      user = FactoryBot.build(:user, email:"arron@example.com")
      user.valid?
      expect(user.errors[:email]).to include("has already been taken")
    end

end

记住:FactoryBot.build 的作用是在内存中创建一个新测试对象;

FactoryBot.create 的作用
是把对象持久存储到应用的测试数据库中。

4.3 使用序列生成唯一的数据

it "does something with multiple users"  do

user1 = FactoryGirl.create(:user)

user2 = FactoryGirl.create(:user)

expect(true).to be_true

end

希望user1,和user2,都可以创建出来,对于唯一属性,可以使用secquence方法来设置,每创建一个对象,唯一属性自动加1.

sequence(:email) { |n| "tester#{n}@example.com" }

4.4 Factory中的 Association

FactoryBod比纯粹的fixtures功能强大的多,可以处理models之间的Association.

bin/rails generate factory_bot:model note

FactoryBot.define do
  factory :note do
    message "My important note" #⚠️写法和在db/migrate中类似。
    association :project
    association :user #关联到user.
  end
end

bin/rails g factory_bot:model project

FactoryBot.define do

factory :project d

sequenct(:name) {|n| "Project #{n}"} #因为为name设置 uniqueness: {scope: :user_id}

description "A test project"

due_on 1.week.from_now

association :owner  #关联到user,因为在模型project.rb中设置了别名

end

end

在用户预构件factories/users.rb中,在第二行,就是为预构件起名的那一行,加上别名
owner。 原因见后面2段文字。

FactoryBot.define do

factory :user, aliases:[:owner] do

因为预构件已经定义:user, :project 和:note,并建立了Association。所以在:

models/note_spec.rb中,只创建了note记录用,同时note.project就创建了project记录并关联上了。

inspect()方法检查是否关联。

  it "generates associated data from a factory" do
    note = FactoryBot.create(:note)
    puts "This note's project is #{note.project.inspect}"
    puts "This note's user is #{note.user.inspect}"
  end

inspect方法 返回所关联的对象。

不过注意⚠️ 预构件可能会骗人,因为它会先创建一个关联的用户(项目的属主),再创建一个用户(记录的属主),因此note.user.inspect返回的对象的email属性将是test2@example.com,  而不是预想的text1@example.com

为了避免这种问题,我们可以修改记录预构件,把两个用户统一:

FactoryBot.define do
  factory :note do
    message "My important note"
    association :project
    user {project.owner}
  end
end

回头看一下app/models/project.rb:Project模型关联的user的别名叫"owner"

belongs_to :owner, class_name: User, foreign_key: :user_id

这就是前面在users.rb加上aliases:[:owner]代码的原因: 让F a c to r y B o t知道可以通过别名"owner"引用预构件user.

4.6 避免预构件中出现重复

同种类型的数据可以定义多个预构件。例如,为了测试项目是按期完成的还是延期了,可以为预构件设定不同的名称,指定不同的属性 .

⚠️需要

Rspec: everyday-rspec实操。FactoryBot预构件 (rspec-expectations gem 查看匹配器) 1-4章的更多相关文章

  1. Rspec: everyday-rspec实操。5:controller test(了解基础)

    第 5 章 控制器测试 5.1基础 rails generate rspec:controller home RSpec.describe HomeController, type: :control ...

  2. Rspec: everyday-rspec实操: 第8章DRY. (6个方法,其中3个方法好上手)

    Don't Repeat Yourself. • 把操作步骤提取到辅助模块中;✅ • 通过let复用测试中的实例变量;✅ • 把通用的设置移到共享的情景中;⚠️(不喜欢) • 在RSpec和rspec ...

  3. Rspec: everyday-rspec实操: 第9章 快速编写测试,编写快速的测试。

    Make it work, make it right, make it fast. 测试运行的时间.应用和测试组件的增长,速度会越来越慢,目标是保持代码的readable, maintainable ...

  4. 百度NLP预训练模型ERNIE2.0最强实操课程来袭!【附教程】

    2019年3月,百度正式发布NLP模型ERNIE,其在中文任务中全面超越BERT一度引发业界广泛关注和探讨.经过短短几个月时间,百度ERNIE再升级,发布持续学习的语义理解框架ERNIE 2.0,及基 ...

  5. .net基础学java系列(四)Console实操

    上一篇文章 .net基础学java系列(三)徘徊反思 本章节没啥营养,请绕路! 看视频,不实操,对于上了年龄的人来说,是记不住的!我已经看了几遍IDEA的教学视频: https://edu.51cto ...

  6. 动手实操(一):如何用七牛云 API 实现相片地图?

    实操玩家: 在苹果手机上,我们只要打开定位服务,拍照后便能在相簿中找到地图,地图上显示着在各地拍摄的相片.网站上这种显示方式也并不少见,例如 Flickr.即将关闭的 Panoramio 等. 作为地 ...

  7. 决策树算法的Python实现—基于金融场景实操

    决策树是最经常使用的数据挖掘算法,本次分享jacky带你深入浅出,走进决策树的世界 基本概念 决策树(Decision Tree) 它通过对训练样本的学习,并建立分类规则,然后依据分类规则,对新样本数 ...

  8. Istio的流量管理(实操三)

    Istio的流量管理(实操三) 涵盖官方文档Traffic Management章节中的egress部分.其中有一小部分问题(已在下文标注)待官方解决. 目录 Istio的流量管理(实操三) 访问外部 ...

  9. 72 个网络应用安全实操要点,全方位保护 Web 应用的安全

    原文地址:Web Application Security Checklist 原文作者:Teo Selenius(已授权) 译者 & 校正:HelloGitHub-小熊熊 & 卤蛋 ...

随机推荐

  1. 20155333 2016-2017-2 《Java程序设计》第九周学习总结

    20155333 2016-2017-2 <Java程序设计>第九周学习总结 教材学习内容总结 JDBC(Java DataBase Connectivity) 驱动的四种类型 JDBC- ...

  2. 519. Random Flip Matrix(Fisher-Yates洗牌算法)

    1. 问题 给定一个全零矩阵的行和列,实现flip函数随机把一个0变成1并返回索引,实现rest函数将所有数归零. 2. 思路 拒绝采样 (1)先计算矩阵的元素个数(行乘以列),记作n,那么[0, n ...

  3. python xml childNodes,childNodes[1].childNodes[0].data例子

    xml: <?xml version='1.0' encoding='utf-8'?><!--this is a test about xml--><booklist t ...

  4. linux基础命令---chattr

    chattr 改变文件的属性,这个命令只有超级用户才能使用.这个指令适用于ext2.ext3.ext4.xfs.ubifs.reiserfs.jfs系统. 此命令的适用范围:RedHat.RHEL.U ...

  5. Jquery图片上传组件,支持多文件上传

    Jquery图片上传组件,支持多文件上传http://www.jq22.com/jquery-info230jQuery File Upload 是一个Jquery图片上传组件,支持多文件上传.取消. ...

  6. Advapi32.dll--介绍

    https://blog.csdn.net/zhoujielun123456/article/details/50338147 使用方法详见:OpsTotalService

  7. IOS学习基础

    http://www.jikexueyuan.com/path/ios/ 界面优化 iOS界面绘图API.控件等知识. 1,绘制图片 2,画板实例 3, 1,UIView的setNeedsDispla ...

  8. 安装PyInstaller打包python

    安装PyInstaller 对于那些网络比较稳定,能够流畅使用pip源地址的用户,直接下面的命令就可以搞定: pip install pyinstaller 通常我们会下载源码包,然后进入包目录,执行 ...

  9. linux下获取本机的获取内网和外网地址

    1.获取内网地址(私有地址) ifconfig -a 2.获取外网地址(公网地址) curl members.3322.org/dyndns/getip

  10. Java ArrayDeque源码剖析

    ArrayDeque 本文github地址 前言 Java里有一个叫做Stack的类,却没有叫做Queue的类(它是个接口名字).当需要使用栈时,Java已不推荐使用Stack,而是推荐使用更高效的A ...