Ruby on rails postgres模式的数据库清理
我有Ruby on rails postgres模式的数据库清理,ruby-on-rails,postgresql,rspec,devise,database-cleaner,Ruby On Rails,Postgresql,Rspec,Devise,Database Cleaner,我有gemdesigne和gemdesigne公寓,我正在使用它们为每个设备的用户帐户创建单独的模式 公寓,建议使用机架中间件在租户之间切换。在这种情况下,这是不可能的(据我所知),因为我让它依赖于用户而不是依赖于请求 除了我的RSpec测试之外,所有的工作都很好。问题是,在每个测试之后,数据库都没有正确地清理(它不会删除新创建用户的模式)。如果我运行一小部分测试,所有测试都会通过,但如果我运行的测试数量超过Faker::Internet。first_name会生成已使用的用户名(无效) 我就是
gemdesigne
和gemdesigne公寓
,我正在使用它们为每个设备的用户帐户创建单独的模式
公寓,建议使用机架中间件在租户之间切换。在这种情况下,这是不可能的(据我所知),因为我让它依赖于用户而不是依赖于请求
除了我的RSpec测试之外,所有的工作都很好。问题是,在每个测试之后,数据库都没有正确地清理(它不会删除新创建用户的模式)。如果我运行一小部分测试,所有测试都会通过,但如果我运行的测试数量超过Faker::Internet。first_name
会生成已使用的用户名(无效)
我就是这样做的:
app/controllers/application\u controller.rb
def scope_tenant
Apartment::Database.switch(current_user.username)
end
after_create :create_schema
private
def create_schema
Apartment::Database.create(self.username)
end
FactoryGirl.define do
factory :user do
username { Faker::Name.first_name }
email { Faker::Internet.email("#{username}") }
password "login_as will not use it anyway"
end
end
Warden.test_mode!
def login_and_switch_schema(user)
login_as(user)
Apartment::Database.switch(user.username) # for some reason `login_as()` didn't do that by itself
end
feature "Album Pages" do
given(:user) { create(:user) }
given(:album) { create(:album) }
around :each do
login_and_switch_schema user
end
scenario...
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
app/controllers/albums\u controller.rb(相册型号属于:用户
)
这是我添加到规格中的内容:
spec/factories/user.rb
def scope_tenant
Apartment::Database.switch(current_user.username)
end
after_create :create_schema
private
def create_schema
Apartment::Database.create(self.username)
end
FactoryGirl.define do
factory :user do
username { Faker::Name.first_name }
email { Faker::Internet.email("#{username}") }
password "login_as will not use it anyway"
end
end
Warden.test_mode!
def login_and_switch_schema(user)
login_as(user)
Apartment::Database.switch(user.username) # for some reason `login_as()` didn't do that by itself
end
feature "Album Pages" do
given(:user) { create(:user) }
given(:album) { create(:album) }
around :each do
login_and_switch_schema user
end
scenario...
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
spec/support/auth_helpers.rb
def scope_tenant
Apartment::Database.switch(current_user.username)
end
after_create :create_schema
private
def create_schema
Apartment::Database.create(self.username)
end
FactoryGirl.define do
factory :user do
username { Faker::Name.first_name }
email { Faker::Internet.email("#{username}") }
password "login_as will not use it anyway"
end
end
Warden.test_mode!
def login_and_switch_schema(user)
login_as(user)
Apartment::Database.switch(user.username) # for some reason `login_as()` didn't do that by itself
end
feature "Album Pages" do
given(:user) { create(:user) }
given(:album) { create(:album) }
around :each do
login_and_switch_schema user
end
scenario...
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
spec/features/albums\u spec.rb
def scope_tenant
Apartment::Database.switch(current_user.username)
end
after_create :create_schema
private
def create_schema
Apartment::Database.create(self.username)
end
FactoryGirl.define do
factory :user do
username { Faker::Name.first_name }
email { Faker::Internet.email("#{username}") }
password "login_as will not use it anyway"
end
end
Warden.test_mode!
def login_and_switch_schema(user)
login_as(user)
Apartment::Database.switch(user.username) # for some reason `login_as()` didn't do that by itself
end
feature "Album Pages" do
given(:user) { create(:user) }
given(:album) { create(:album) }
around :each do
login_and_switch_schema user
end
scenario...
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
因为我有一些关于js:true
的测试,所以我有:
spec/support/database\u cleaner.rb
def scope_tenant
Apartment::Database.switch(current_user.username)
end
after_create :create_schema
private
def create_schema
Apartment::Database.create(self.username)
end
FactoryGirl.define do
factory :user do
username { Faker::Name.first_name }
email { Faker::Internet.email("#{username}") }
password "login_as will not use it anyway"
end
end
Warden.test_mode!
def login_and_switch_schema(user)
login_as(user)
Apartment::Database.switch(user.username) # for some reason `login_as()` didn't do that by itself
end
feature "Album Pages" do
given(:user) { create(:user) }
given(:album) { create(:album) }
around :each do
login_and_switch_schema user
end
scenario...
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
所有源的当前提交可在上获得
所以。。主要的问题是:如何在测试后清理为每个用户创建的数据库模式?我也将感谢您的任何其他评论。提前感谢您的帮助。这并不是公寓特有的,而是与DatabaseCleaner如何清理数据库有关。使用事务时,在该事务中创建的任何模式也将回滚。然而,不幸的是,您需要截断功能规格,因为事务不起作用(不要尝试共享连接“解决方案”,它会由于竞争条件导致随机失败)。因此,考虑到这一点,对于您的特性规范,您需要一种方法来确保删除创建的任何模式,因为截断只会截断表,而不会清理模式 我建议隔离专门测试多租户行为的特性规范,以确保它按照您希望的方式工作,并手动清理这些规范中创建的任何模式。然后,对于其余的特性规范,假设您在一个租户中进行测试,或者在您的情况下,在一个用户中进行测试 我们在测试套件中这样做,其中一个新的
公司
模型创建一个新的租户
。因此,我们对多个租户测试这种行为,然后对我们在一家公司内运行的其他功能进行测试,这样我们就不必再担心清理问题,只需截断该1个租户内的表即可。Truncate将始终截断当前租户中的表,除非您有排除的\u模型
这有帮助吗?这并不是公寓特有的,而是与DatabaseCleaner如何清理数据库有关。使用事务时,在该事务中创建的任何模式也将回滚。然而,不幸的是,您需要截断功能规格,因为事务不起作用(不要尝试共享连接“解决方案”,它会由于竞争条件导致随机失败)。因此,考虑到这一点,对于您的特性规范,您需要一种方法来确保删除创建的任何模式,因为截断只会截断表,而不会清理模式 我建议隔离专门测试多租户行为的特性规范,以确保它按照您希望的方式工作,并手动清理这些规范中创建的任何模式。然后,对于其余的特性规范,假设您在一个租户中进行测试,或者在您的情况下,在一个用户中进行测试 我们在测试套件中这样做,其中一个新的
公司
模型创建一个新的租户
。因此,我们对多个租户测试这种行为,然后对我们在一家公司内运行的其他功能进行测试,这样我们就不必再担心清理问题,只需截断该1个租户内的表即可。Truncate将始终截断当前租户中的表,除非您有排除的\u模型
这有帮助吗?目前处理截断和多租户应用程序的另一种方法是创建租户并在每次测试中删除它。像这样: 在rails_helper.rb上:
...
config.before(:suite) do
DatabaseCleaner.clean_with :truncation
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
Apartment::Tenant.drop('test') rescue nil
Company.create! name: "LittleCompany", subdomain: "test"
DatabaseCleaner.start
Apartment::Tenant.switch! "test"
end
config.after(:each) do
Apartment::Tenant.reset
DatabaseCleaner.clean
Apartment::Tenant.drop('test')
end
...
这当然不是很快,但这是我找到的唯一方法。目前处理截断和多租户应用程序的另一种方法是创建租户并在每次测试中删除它。像这样: 在rails_helper.rb上:
...
config.before(:suite) do
DatabaseCleaner.clean_with :truncation
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
Apartment::Tenant.drop('test') rescue nil
Company.create! name: "LittleCompany", subdomain: "test"
DatabaseCleaner.start
Apartment::Tenant.switch! "test"
end
config.after(:each) do
Apartment::Tenant.reset
DatabaseCleaner.clean
Apartment::Tenant.drop('test')
end
...
当然,这不是很快,但这是我找到的唯一方法。如何使用“drop schema X cascade;”??这将删除模式,然后可以重新创建一个空白模式。现在我再次阅读了描述,也许模式在rails中的含义有所不同?在postgres中,您只需删除它,它就会消失(如果您知道它的名称的话)??这将删除模式,然后可以重新创建一个空白模式。现在我再次阅读了描述,也许模式在rails中的含义有所不同?在postgres中,你可以简单地放弃它,它就会消失(如果你知道它的名字的话)。很多!非常感谢。我在另一个答案中描述了最终的解决方案。(我决定不拆分使用多租户的功能规格,因为我认为这对其中许多功能规格至关重要。例如,我有一些测试,检查数据在用户/租户范围内是否唯一)很多!非常感谢。我在另一个答案中描述了最终的解决方案。(我决定不拆分使用多租户的功能规格,因为我认为这对其中许多功能规格至关重要。例如,我有一些测试,检查数据在用户/租户的范围内是否唯一)