DatabaseCleaner 不重置 rails 测试单元中的自动增量索引
DatabaseCleaner don't reset autoincrement index in rails test unit
test/test_helper.rb :
ENV["RAILS_ENV"] ||= "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'database_cleaner'
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation, pre_count: true, reset_ids: true)
class ActiveSupport::TestCase
ActiveRecord::Migration.check_pending!
def setup
DatabaseCleaner.start
end
def teardown
DatabaseCleaner.clean
p '-------- DB Cleaned ---------'
end
end
我的测试单元文件:(测试 1 和 2 是重复的)
require 'test_helper'
class ItemTest < ActiveSupport::TestCase
test "test1" do
i = Item.create!
p ActiveRecord::Base.connection.execute("SELECT auto_increment FROM information_schema.tables WHERE table_schema = 'tmi_game_test' AND table_name = 'items';").first
assert_equal 1, Item.count
assert_equal 1, i.id
end
test "test2" do
i = Item.create!
p ActiveRecord::Base.connection.execute("SELECT auto_increment FROM information_schema.tables WHERE table_schema = 'tmi_game_test' AND table_name = 'items';").first
assert_equal 1, Item.count
assert_equal 1, i.id
end
end
结果:
# Running:
[2]
"-------- DB Cleaned ---------"
.[3]
"-------- DB Cleaned ---------"
F
Finished in 0.142886s, 13.9972 runs/s, 27.9944 assertions/s.
1) Failure:
ItemTest#test_test2 [test/models/item_test.rb:45]:
Expected: 1
Actual: 2
2 runs, 4 assertions, 1 failures, 0 errors, 0 skips
为什么不起作用?我的错误在哪里?
这是预期的行为。您正在使用 :transaction
策略来清理表。这意味着每个测试都包含在一个交易中,该交易在测试后(在 teardown
期间)被 ROLLBACK
编辑。
您没有说明您使用的是哪个数据库,但是 ROLLBACK
没有重置 AUTO_INCREMENT
值 ,MySQL 中也没有(参见 bug #6714) nor in PostgreSQL (see bug #1139).
根据这个 SO answer,我认为 你不应该在测试中依赖 auto_increment ID 值 。我认为您应该测试其他属性,而不是断言您正在使用预期的记录。
如果您确实需要重置 AUTO_INCREMENT
计数器,请改用 :truncation
清理策略。 IE。删除 clean_with
行并将策略设置为 :truncation
。虽然它比交易慢得多。
这是一个相当古老的 post 但我在寻找从截断策略切换到删除策略的方法时偶然发现了它。我这样做是因为截断策略让我们的规范变得毫无意义。它在少量表上运行良好,我们已经增加到 44 个表,现在它变得越来越慢。
最终,Matouš 之前的评论是 你不应该在测试中依赖 auto_increment ID 值 是正确的。如果我能回到过去并告诉更改为截断策略的人不要这样做,那么我会的。 las,我无法进行时间旅行并修复我们的规格,以便 ids 在这个时间点无关紧要是一项艰巨的任务。我只是想加快我们的规格,所以我设计了以下解决方案。
为了让它工作,我在我的 spec_helper 中添加了以下内容,这仅适用于 Mysql:
require 'database_cleaner/active_record/truncation'
require 'database_cleaner/active_record/deletion'
module DatabaseCleaner
module ActiveRecord
class Deletion < Truncation
def delete_table connection, table_name
connection.execute("DELETE FROM #{connection.quote_table_name(table_name)}")
connection.execute("ALTER TABLE #{table_name} AUTO_INCREMENT = 1;")
end
end
end
end
如果您希望它与 postgres 一起使用,请替换:
connection.execute("ALTER TABLE #{table_name} AUTO_INCREMENT = 1;")
有
connection.reset_pk_sequence!(table_name)
如果您希望它与 sqlite 一起使用,请替换为:
connection.execute("delete from sqlite_sequence where name='#{table_name}';")
我还创建了一个 PR 以使其成为一个策略选项:
https://github.com/DatabaseCleaner/database_cleaner-active_record/pull/71
但我怀疑这会被拒绝,因为这都是坏事而不是好事。如果您必须重置 id,请为个别规格执行此操作。
test/test_helper.rb :
ENV["RAILS_ENV"] ||= "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'database_cleaner'
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation, pre_count: true, reset_ids: true)
class ActiveSupport::TestCase
ActiveRecord::Migration.check_pending!
def setup
DatabaseCleaner.start
end
def teardown
DatabaseCleaner.clean
p '-------- DB Cleaned ---------'
end
end
我的测试单元文件:(测试 1 和 2 是重复的)
require 'test_helper'
class ItemTest < ActiveSupport::TestCase
test "test1" do
i = Item.create!
p ActiveRecord::Base.connection.execute("SELECT auto_increment FROM information_schema.tables WHERE table_schema = 'tmi_game_test' AND table_name = 'items';").first
assert_equal 1, Item.count
assert_equal 1, i.id
end
test "test2" do
i = Item.create!
p ActiveRecord::Base.connection.execute("SELECT auto_increment FROM information_schema.tables WHERE table_schema = 'tmi_game_test' AND table_name = 'items';").first
assert_equal 1, Item.count
assert_equal 1, i.id
end
end
结果:
# Running:
[2]
"-------- DB Cleaned ---------"
.[3]
"-------- DB Cleaned ---------"
F
Finished in 0.142886s, 13.9972 runs/s, 27.9944 assertions/s.
1) Failure:
ItemTest#test_test2 [test/models/item_test.rb:45]:
Expected: 1
Actual: 2
2 runs, 4 assertions, 1 failures, 0 errors, 0 skips
为什么不起作用?我的错误在哪里?
这是预期的行为。您正在使用 :transaction
策略来清理表。这意味着每个测试都包含在一个交易中,该交易在测试后(在 teardown
期间)被 ROLLBACK
编辑。
您没有说明您使用的是哪个数据库,但是 ROLLBACK
没有重置 AUTO_INCREMENT
值 ,MySQL 中也没有(参见 bug #6714) nor in PostgreSQL (see bug #1139).
根据这个 SO answer,我认为 你不应该在测试中依赖 auto_increment ID 值 。我认为您应该测试其他属性,而不是断言您正在使用预期的记录。
如果您确实需要重置 AUTO_INCREMENT
计数器,请改用 :truncation
清理策略。 IE。删除 clean_with
行并将策略设置为 :truncation
。虽然它比交易慢得多。
这是一个相当古老的 post 但我在寻找从截断策略切换到删除策略的方法时偶然发现了它。我这样做是因为截断策略让我们的规范变得毫无意义。它在少量表上运行良好,我们已经增加到 44 个表,现在它变得越来越慢。
最终,Matouš 之前的评论是 你不应该在测试中依赖 auto_increment ID 值 是正确的。如果我能回到过去并告诉更改为截断策略的人不要这样做,那么我会的。 las,我无法进行时间旅行并修复我们的规格,以便 ids 在这个时间点无关紧要是一项艰巨的任务。我只是想加快我们的规格,所以我设计了以下解决方案。
为了让它工作,我在我的 spec_helper 中添加了以下内容,这仅适用于 Mysql:
require 'database_cleaner/active_record/truncation'
require 'database_cleaner/active_record/deletion'
module DatabaseCleaner
module ActiveRecord
class Deletion < Truncation
def delete_table connection, table_name
connection.execute("DELETE FROM #{connection.quote_table_name(table_name)}")
connection.execute("ALTER TABLE #{table_name} AUTO_INCREMENT = 1;")
end
end
end
end
如果您希望它与 postgres 一起使用,请替换:
connection.execute("ALTER TABLE #{table_name} AUTO_INCREMENT = 1;")
有
connection.reset_pk_sequence!(table_name)
如果您希望它与 sqlite 一起使用,请替换为:
connection.execute("delete from sqlite_sequence where name='#{table_name}';")
我还创建了一个 PR 以使其成为一个策略选项: https://github.com/DatabaseCleaner/database_cleaner-active_record/pull/71
但我怀疑这会被拒绝,因为这都是坏事而不是好事。如果您必须重置 id,请为个别规格执行此操作。