在测试这项范例之前我一直搞不懂在过去测试的时候我记得 test db 不会清除资料,後来查资料才发现原来在安装 rspec 时在 rails_helper.rb
预设会有一行指令
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
这行指令会帮你在进行测试的时候将 it
里建立的资料在 it
结束时进行 rollback,以至於在进行测试的时候不会出错,但他只有在测试的时候才会执行
# user_spec.rb
require 'rails_helper'
RSpec.describe User, type: :model do
describe 'validations' do
it { is_expected.to validate_presence_of(:name) }
end
describe 'count user' do
let(:user) { User.create!(name: 'test1') }
it 'no user' do
expect(User.count).to eq(0)
end
it 'one user' do
user
expect(User.count).to eq(1)
end
end
end
# rspec spec/models/user_spec.rb
D, [2021-09-08T22:26:59.270813 #35829] DEBUG -- : (1.4ms) SELECT sqlite_version(*)
D, [2021-09-08T22:26:59.288039 #35829] DEBUG -- : (1.2ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? [["key", "schema_sha1"]]
D, [2021-09-08T22:26:59.290522 #35829] DEBUG -- : (0.1ms) SELECT sqlite_version(*)
D, [2021-09-08T22:26:59.296831 #35829] DEBUG -- : (0.5ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
D, [2021-09-08T22:26:59.320237 #35829] DEBUG -- : TRANSACTION (0.3ms) begin transaction
D, [2021-09-08T22:26:59.363110 #35829] DEBUG -- : User Exists? (0.4ms) SELECT 1 AS one FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", ""], ["LIMIT", 1]]
D, [2021-09-08T22:26:59.369438 #35829] DEBUG -- : User Exists? (0.1ms) SELECT 1 AS one FROM "users" WHERE "users"."name" IS NULL LIMIT ? [["LIMIT", 1]]
D, [2021-09-08T22:26:59.424097 #35829] DEBUG -- : TRANSACTION (0.1ms) rollback transaction
.D, [2021-09-08T22:26:59.426800 #35829] DEBUG -- : TRANSACTION (0.1ms) begin transaction
D, [2021-09-08T22:26:59.427675 #35829] DEBUG -- : (0.2ms) SELECT COUNT(*) FROM "users"
D, [2021-09-08T22:26:59.429779 #35829] DEBUG -- : TRANSACTION (0.1ms) rollback transaction
.D, [2021-09-08T22:26:59.433351 #35829] DEBUG -- : TRANSACTION (0.3ms) begin transaction
D, [2021-09-08T22:26:59.434656 #35829] DEBUG -- : (0.3ms) SELECT COUNT(*) FROM "users"
D, [2021-09-08T22:26:59.435220 #35829] DEBUG -- : TRANSACTION (0.1ms) rollback transaction
.
Finished in 0.13114 seconds (files took 4 seconds to load)
3 examples, 0 failures
看 log 会发现进行了 3次 rollback transaction
但如果你设成false
config.use_transactional_fixtures = false
执行第一次会正确,但执行第二次时会出现错误
D, [2021-09-08T22:30:32.644791 #36065] DEBUG -- : (1.3ms) SELECT sqlite_version(*)
D, [2021-09-08T22:30:32.664166 #36065] DEBUG -- : (1.3ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? [["key", "schema_sha1"]]
D, [2021-09-08T22:30:32.666283 #36065] DEBUG -- : (0.1ms) SELECT sqlite_version(*)
D, [2021-09-08T22:30:32.670514 #36065] DEBUG -- : (0.4ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
D, [2021-09-08T22:30:32.716741 #36065] DEBUG -- : User Exists? (0.6ms) SELECT 1 AS one FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", ""], ["LIMIT", 1]]
D, [2021-09-08T22:30:32.720539 #36065] DEBUG -- : User Exists? (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."name" IS NULL LIMIT ? [["LIMIT", 1]]
.D, [2021-09-08T22:30:32.752356 #36065] DEBUG -- : (0.2ms) SELECT COUNT(*) FROM "users"
FD, [2021-09-08T22:30:32.778396 #36065] DEBUG -- : TRANSACTION (0.1ms) begin transaction
D, [2021-09-08T22:30:32.778836 #36065] DEBUG -- : User Exists? (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", "test1"], ["LIMIT", 1]]
D, [2021-09-08T22:30:32.780966 #36065] DEBUG -- : TRANSACTION (0.1ms) rollback transaction
F
Failures:
1) User count user no user
Failure/Error: expect(User.count).to eq(0)
expected: 0
got: 1
(compared using ==)
# ./spec/models/user_spec.rb:11:in `block (3 levels) in <top (required)>'
2) User count user one user
Failure/Error: let(:user) { User.create!(name: 'test1') }
ActiveRecord::RecordInvalid:
Validation failed: Name has already been taken
# ./spec/models/user_spec.rb:9:in `block (3 levels) in <top (required)>'
# ./spec/models/user_spec.rb:15:in `block (3 levels) in <top (required)>'
Finished in 0.10143 seconds (files took 3.88 seconds to load)
3 examples, 2 failures
Failed examples:
rspec ./spec/models/user_spec.rb:10 # User count user no user
rspec ./spec/models/user_spec.rb:14 # User count user one user
这次在 it 之间并没有 rollback,而且我们有设定 validates :name, presence: true, uniqueness: true
所以才会出现 Name has already been taken
错误
所以在 it 'no user'
的案例时就会捞到上次执行 rspec 时所残留的资料造成此处的 User.count
为 1,我们到 console 看也能发现确实有资料并未被清除
#rails console -e test
2.6.6 :001 > User.all
D, [2021-09-08T22:35:51.565100 #36206] DEBUG -- : (0.7ms) SELECT sqlite_version(*)
D, [2021-09-08T22:35:51.566758 #36206] DEBUG -- : User Load (0.3ms) SELECT "users".* FROM "users" /* loading for inspect */ LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 2, created_at: "2021-09-08 14:32:44.779978000 +0000", updated_at: "2021-09-08 14:32:44.779978000 +0000", name: "test1", email: nil, phone: nil>]>
不过请记得他是在测试时将 it
里建立的资料使用完 rollback 回去 所以我们如果到 console 直接建立一笔资料...
#rails console -e test
2.6.6 :001 > User.create!(name: 'test1')
D, [2021-09-08T22:40:44.682262 #36410] DEBUG -- : (0.8ms) SELECT sqlite_version(*)
D, [2021-09-08T22:40:44.707086 #36410] DEBUG -- : TRANSACTION (0.1ms) begin transaction
D, [2021-09-08T22:40:44.707786 #36410] DEBUG -- : User Exists? (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", "test1"], ["LIMIT", 1]]
D, [2021-09-08T22:40:44.710169 #36410] DEBUG -- : User Create (0.5ms) INSERT INTO "users" ("created_at", "updated_at", "name") VALUES (?, ?, ?) [["created_at", "2021-09-08 14:40:44.708173"], ["updated_at", "2021-09-08 14:40:44.708173"], ["name", "test1"]]
D, [2021-09-08T22:40:44.713018 #36410] DEBUG -- : TRANSACTION (2.2ms) commit transaction
=> #<User id: 3, created_at: "2021-09-08 14:40:44.708173000 +0000", updated_at: "2021-09-08 14:40:44.708173000 +0000", name: "test1", email: nil, phone: nil>
执行 rspec spec/models/user_spec.rb
D, [2021-09-08T22:41:15.880642 #36446] DEBUG -- : (0.6ms) SELECT sqlite_version(*)
D, [2021-09-08T22:41:15.889610 #36446] DEBUG -- : (0.2ms) SELECT "ar_internal_metadata"."value" FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? [["key", "schema_sha1"]]
D, [2021-09-08T22:41:15.891983 #36446] DEBUG -- : (0.1ms) SELECT sqlite_version(*)
D, [2021-09-08T22:41:15.896092 #36446] DEBUG -- : (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
D, [2021-09-08T22:41:15.911299 #36446] DEBUG -- : TRANSACTION (0.1ms) begin transaction
D, [2021-09-08T22:41:15.934959 #36446] DEBUG -- : User Exists? (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", ""], ["LIMIT", 1]]
D, [2021-09-08T22:41:15.938151 #36446] DEBUG -- : User Exists? (0.1ms) SELECT 1 AS one FROM "users" WHERE "users"."name" IS NULL LIMIT ? [["LIMIT", 1]]
D, [2021-09-08T22:41:15.974051 #36446] DEBUG -- : TRANSACTION (0.1ms) rollback transaction
.D, [2021-09-08T22:41:15.975775 #36446] DEBUG -- : TRANSACTION (0.1ms) begin transaction
D, [2021-09-08T22:41:15.976678 #36446] DEBUG -- : (0.2ms) SELECT COUNT(*) FROM "users"
D, [2021-09-08T22:41:15.998021 #36446] DEBUG -- : TRANSACTION (0.1ms) rollback transaction
FD, [2021-09-08T22:41:15.999450 #36446] DEBUG -- : TRANSACTION (0.2ms) begin transaction
D, [2021-09-08T22:41:16.002627 #36446] DEBUG -- : TRANSACTION (0.2ms) SAVEPOINT active_record_1
D, [2021-09-08T22:41:16.003363 #36446] DEBUG -- : User Exists? (0.4ms) SELECT 1 AS one FROM "users" WHERE "users"."name" = ? LIMIT ? [["name", "test1"], ["LIMIT", 1]]
D, [2021-09-08T22:41:16.005467 #36446] DEBUG -- : TRANSACTION (0.1ms) ROLLBACK TO SAVEPOINT active_record_1
D, [2021-09-08T22:41:16.006180 #36446] DEBUG -- : TRANSACTION (0.1ms) rollback transaction
F
Failures:
1) User count user no user
Failure/Error: expect(User.count).to eq(0)
expected: 0
got: 1
(compared using ==)
# ./spec/models/user_spec.rb:11:in `block (3 levels) in <top (required)>'
2) User count user one user
Failure/Error: let(:user) { User.create!(name: 'test1') }
ActiveRecord::RecordInvalid:
Validation failed: Name has already been taken
# ./spec/models/user_spec.rb:9:in `block (3 levels) in <top (required)>'
# ./spec/models/user_spec.rb:15:in `block (3 levels) in <top (required)>'
Finished in 0.10111 seconds (files took 3.64 seconds to load)
3 examples, 2 failures
Failed examples:
rspec ./spec/models/user_spec.rb:10 # User count user no user
rspec ./spec/models/user_spec.rb:14 # User count user one user
确实有进行 rollback 但他并不能把 console 建立的 user 删除,而且有时候在测试的时候也会到 console 建立测试资料看看是否正确,如果真的要清乾净的话这时就可以安装 Database Cleaner
!
官方文件有写到
Instead of using the database_cleaner gem directly, each ORM has its own gem. Most projects will only need the database_cleaner-active_record gem
所以只要安装这个即可 gem 'database_cleaner-active_record'
并且依照官方文件可以在spec_helper.rb
# spec_helper.rb
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.around(:each) do |example|
DatabaseCleaner.cleaning do
example.run
end
end
我们在每个测试档案执行之前设定 DatabaseCleaner
的清除方式且在每个 example
(每个it) 执行之前进行清除资料库的动作。
我们就能把 config.use_transactional_fixtures
设为 false
执行後确实测试通过且资料都会被清除
至於 DatabaseCleaner.strategy = :transaction
官方提到有三种策略可以清除 :transaction
、Deletion
、Truncation
差异在於
transaction
(官方提及可以优先使用)=> 执行的 SQL 包在 BEGIN TRANSACTION
里然後用 rollback 回初始状态
truncation
=> 直接在资料库 TRUNCATE TABLE
把 table 清空
deletion
=> 直接在资料库 下 SQL语法 DELETE FROM
删除资料,速度较慢
今天就介绍到这边,明天开始介绍 FactoryBot !
参考来源:
>>: Day 8. Hashicorp Nomad: Application Logs
通用标准(ISO 15408)指定了评估IT产品而不是供应商资格的标准。 -通用标准评估 FOCI(...
CSS是什麽? CSS,全名为Cascading Style Sheets,中文为阶层式样式表。跟H...
闲话家常 今天来到了铁人赛的第四天了,我们继续来聊聊资料结构,秉持着一天弄懂一个观念,相信30天的铁...
一日客语:中文:圆 客语: 眼ienˇ 学习内容 检查实例的建构器类型:instanceof、con...
创建App-讯息界面 今天进行讯息界面,本界面会使用Button、Text Field等基本UI来建...