总文档连接:

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. 【百度统计】设置页面元素点击事件转化pv、uv

    html元素点击事件内添加代码:_hmt.push(['_trackEvent', category, action, opt_label, opt_value]); 1. '_trackEvent' ...

  2. click 在网页测试手机模式下无效,不能执行。调成非手机模式即可

    click  在网页测试手机模式下无效,不能执行. 调成非手机模式即可

  3. php ci 报错 Object not found! The requested URL was not found on this server. If you entered the URL manually please check

    Object not found! The requested URL was not found on this server. The link on the referring page see ...

  4. 20145306 张文锦 网络攻防 web基础

    20145306 网络攻防 web基础 实验内容 WebServer:掌握Apache启停配置修改(如监听端口)前端编程:熟悉HTML+JavaScript.了解表单的概念编写不同的HTML网页,放入 ...

  5. getContext,getApplicationContext和this有什么区别

    使用this, 说明当前类是context的子类,一般是activity application等使用getApplicationContext 取得的是当前app所使用的application,这在 ...

  6. Bootstrap 使用教程 与jQuery的Ajax方法

    jQuery.ajax(url,[settings]) 更加详细的内容参考    jQuery API 中文在线手册 概述 通过 HTTP 请求加载远程数据. jQuery 底层 AJAX 实现.简单 ...

  7. sublimeText3最新教程-自带插件汉化(sublime-text_build-3175_amd64)

    一.可用注册码 1.更改dns 在linux下的目录是     /etc/hosts 在win7中,hosts文件的位置:C:\Windows\System32\drivers\etc 127.0.0 ...

  8. 再谈树---无根树转有根树( dfs搜索转化+fa数组记录父节点) *【模板】

    #include <stdio.h> #include <string.h> #include <stdlib.h> #include <vector> ...

  9. The destination you provided is not a full refname (i.e., starting with "refs/")

    $ git push v5 v5/hotfix/5.1:hotfix/5.1-quartzerror: The destination you provided is not a full refna ...

  10. 软件设计师真题试题&&答案

    软件设计师2013上半年上午试题及答案详解 下午试题 2013下半年上午答案 试题 下午答案 试题 2014上半年上午答案 2015上半年上午答案 2016上半年上午试题