D-17. Rspec 从零开始写测试(一)

万事起头难,有些事常常是不知道怎麽开始。


安装於Project

$ rails new project_name -T

省略原生Test

$ gem 'rspec-rails', :group => [:development, :test]
$ bundle

产生资源

$ rails generate rspec:install
create  .rspec
create  spec
create  spec/spec_helper.rb
create  spec/rails_helper.rb
#一资料夹两个档案,一git版控档案。

最基础指令

$ rspec  #目录底下的测试档一次跑。
$ rspec spec/models/????_spec.rb #指定跑单一档。
$ rails generate --help | grep rspec #查可以帮助开哪些测试档,看了会很後悔?

拿到任何专案,先给他rspec啦!!!


先从model开始吧。

准备工作。

$ rails g model role name job age:integer

create    db/migrate/20210909122636_create_roles.rb
create    app/models/role.rb
invoke    rspec
create    spec/models/role_spec.rb
# migrate、model、spec个一。

$ rails db:create
Created database 'rspec_test_development'
Created database 'rspec_test_test'
$ rails db:migrate
== 20210909122636 CreateRoles: migrating ======================================
-- create_table(:roles)
   -> 0.0084s
== 20210909122636 CreateRoles: migrated (0.0084s) =============================

认识一下空白测试档。

确认spec/models/role_spec.rb,如未变动任何设定应该长得如下

require 'rails_helper'

RSpec.describe Role, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"
end

pending让你回头测试用,有时有些方法甚至整个想测试的单元都来不及测试,会用pending处理。目前的写法就是整个Classpending

单一方法需要pending

require 'rails_helper'

RSpec.describe Role, type: :model do
  describe "#some_method" do
    pending
  end
end
#比较常见
RSpec.describe Role, type: :model do
  pending "#some_method"
end

如果是一个方法内还有问题要处理。

RSpec.describe Role, type: :model do
  describe "#some_method" do
    it "计算值不能大於100" do
    end
    pending "常常大於100还没处理"
  end
end

这是rspec画面。

.*  //有一个绿灯正确,因为do..end了,只是有待处理事项

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) Role#some_method 常常大於100还没处理
     # Not yet implemented
     # ./spec/models/role_spec.rb:7


Finished in 0.00882 seconds (files took 0.74709 seconds to load)
2 examples, 0 failures, 1 pending

这篇文章我会先把pending先移掉,网路上的教学五花八门,可能每家公司的规范也五花八门虽然我还在家里宅不是很确定,所以看完本文还请多看文件,至少文件内用法一定正确。


测试验证(Validations)

先用Validations练手一下。

以下测试单纯安装rspec-rails而已,下篇会介绍其他辅助gem

require 'rails_helper'

RSpec.describe Role, type: :model do
  describe "validates" do
    it "name不能空白"
    it "job不能块白"
    it "age不能空白"
  end
end

rspec画面。

***

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) Role validates name不能空白
     # Not yet implemented
     # ./spec/models/role_spec.rb:5

  2) Role validates job不能块白
     # Not yet implemented
     # ./spec/models/role_spec.rb:6

  3) Role validates age不能空白
     # Not yet implemented
     # ./spec/models/role_spec.rb:7


Finished in 0.0017 seconds (files took 0.77157 seconds to load)
3 examples, 0 failures, 3 pending

还是3个*,因为我只有写it与期望内容,连测试物件都没有。

修改如下

require 'rails_helper'

RSpec.describe Role, type: :model do

  describe "测试验证功能" do   #能越清楚越好,英文不好所以我用中文。
    context "所有栏位不能空白" do

      let(:role) {Role.new} #放自己写的`describe`外即可,此处故意放里面。

      it "name不可以空白" do
        role.name = nil
        expect(role).to_not be_valid
      end

      it "job不可以空白" do
        role.job = nil
        expect(role).to_not be_valid
      end

      it "age不可以空白" do
        role.age = nil
        expect(role).to_not be_valid
      end
    end
  end
end

一样先看rspec画面

FFF

Failures:

  1) Role 测试验证功能 所有栏位不能空白 name不可以空白
     Failure/Error: expect(role).to_not be_valid
       expected #<Role id: nil, name: nil, job: nil, age: nil, created_at: nil, updated_at: nil> not to be valid
     # ./spec/models/role_spec.rb:12:in `block (4 levels) in <top (required)>

  2) Role 测试验证功能 所有栏位不能空白 job不可以空白
     Failure/Error: expect(role).to_not be_valid
       expected #<Role id: nil, name: nil, job: nil, age: nil, created_at: nil, updated_at: nil> not to be valid
     # ./spec/models/role_spec.rb:17:in `block (4 levels) in <top (required)>

  3) Role 测试验证功能 所有栏位不能空白 age不可以空白
     Failure/Error: expect(role).to_not be_valid
       expected #<Role id: nil, name: nil, job: nil, age: nil, created_at: nil, updated_at: nil> not to be valid
     # ./spec/models/role_spec.rb:22:in `block (4 levels) in <top (required)>

Finished in 0.02345 seconds (files took 0.7201 seconds to load)
3 examples, 3 failures

Failed examples:

rspec ./spec/models/role_spec.rb:10 # Role 测试验证功能 所有栏位不能空白 name不可以空白
rspec ./spec/models/role_spec.rb:15 # Role 测试验证功能 所有栏位不能空白 job不可以空白
rspec ./spec/models/role_spec.rb:20 # Role 测试验证功能 所有栏位不能空白 age不可以空白

三个F,因为我还没在`models/role.rb写验证。

describe目前我是拿来描述一个大功能。
context目前我将几种属於同状况的测试包在一起。

letbefore

before写法

describe "#????" do
  before(:each) do
    @abc = Abc.new( #let与before都可以带参数 )
  end
end

before(:each) 每个it之前执行,可以只写 before。
before(:all) 一个RSpec.describe只执行一次。
也有after
after(:each) 每段it之後执行。
after(:all) 一个RSpec.describe後只执行一次。

let

let == before(:each),写法不同外,let後面的block,会在需使用时才执行,具有lazy loading的效果,提高测试效能,不过以会写优先,有兴趣者可以看看这篇解答:https://stackoverflow.com/questions/5974360/rspec-what-is-the-difference-between-let-and-a-before-block 目前写短短的,不会有感觉啦XD

toorto_not & be_valid

be_valid可验证,所以to be_valid可验证,to_not be_valid不可验证。
基本语法还是请多看Rspec的使用手册。


我先加入Rails本身内建验证器。

app/models/role.rb

class Role < ApplicationRecord
  validates :name, presence: true
  validates :job, presence: true
  validates :age, presence: true
end

rspec画面

...

Finished in 0.0185 seconds (files took 0.77751 seconds to load)
3 examples, 0 failures

通过了!...明天介绍用Gem辅助。

今日的code。https://github.com/nauosika/Rspec_test/tree/D_17_Rspec_content


今天的leetcode.1512. Number of Good Pairs
题目连结:https://leetcode.com/problems/number-of-good-pairs/
题目重点:A pair (i, j) is called good if nums[i] == nums[j] and i < j.
想办法纪录这件事情发生的次数就可以了。

# @param {Integer[]} nums
# @return {Integer}
def num_identical_pairs(nums)

end

puts num_identical_pairs([1,2,3,1,1,3]) #=> 4
puts num_identical_pairs([1,1,1,1]) #=> 6
puts num_identical_pairs([1,2,3]) #=> 0

其实双回圈这件事总是会让我卡住。

2.7.3 :062 > [1, 2, 3].each do |i|
2.7.3 :063 >   [1, 2, 3].each do |j|
2.7.3 :064 >     p [i, j]
2.7.3 :065 >   end
2.7.3 :066 > end
[1, 1]
[1, 2]
[1, 3]
[2, 1]
[2, 2]
[2, 3]
[3, 1]
[3, 2]
[3, 3]
  => [1, 2, 3]

如过还记得双回圈那就好办了。

def num_identical_pairs(nums)
  pairs = 0
  nums.each_with_index do |num1, i|
    nums.each_with_index do |num2, j|
      if num1 == num2 && i < j
        pairs += 1
      end
    end
  end
  pairs
end

不过这题直接用timesfor回圈比较更简单明了。

def num_identical_pairs(nums)
  pairs = 0
  nums.size.times do |i|
    nums.size.times do |j|
      if nums[i] == nums [j] && i < j
        pairs += 1
      end
    end
  end
  pairs
end

2.7.3 :067 > [1,2,3,1,1,3].size.times do |i|
2.7.3 :068 >   puts i
2.7.3 :069 > end
0
1
2
3
4
5
 => 6

今天...没有提到面试题。


<<:  Day4 如何安装Git的环境

>>:  Day 13:RecyclerView 基本资料列表显示

Day 07- Blocks

Objective-C class 定义一个物件(object),该物件含有资料和一些行为。有时可能...

Day 11 彩色黑白渐层照片

彩色黑白渐层照片 教学原文参考:彩色黑白渐层照片 这篇文章会介绍使用 GIMP 的图层混合模式、渐层...

基础的Git上传方法与指令

1.以下程序码中有中文的,一律要更换成自己要的名字 2.程序码中有许多空格,请务必注意不要漏掉!! ...

Day 21 [Python ML、资料视觉化] 长条图和热度图

设定jupyter notebook import pandas as pd pd.plotting...