Rails 3 + rspec + postgres:如何针对具有外键约束的数据库运行测试套件?

时间:2014-06-27 14:26:22

标签: ruby-on-rails postgresql rspec foreign-keys rspec-rails

我有一个使用PostgreSQL 9.1作为数据库的Rails 3应用程序。我们使用RSpec和FactoryGirl进行测试。

数据模型在1对多或多对多关系中有许多模型,这些约束使用has_many,belongs_to,has_many:through等等在Rails模型中编码。我们的代码看起来像:

class User < ActiveRecord::Base
   attr_accessible :name
   has_many :phones
end

class Phone < ActiveRecord::Base
   attr_accessible :number, user_id
   belongs_to :user
end

PostgreSQL中的模式看起来像这样

CREATE TABLE users (id integer, name VARCHAR(20));
CREATE TABLE phones (id integer, number VARCHAR(20), user_id integer);

但是,我更喜欢使用外键约束而不是仅在模型中对数据库中的数据约束进行编码。为此,我创建了一个Rails迁移并添加了外键约束,例如:

sql = "ALTER TABLE phones ADD CONSTRAINT phones_user_id FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE RESTRICT;"
ActiveRecord::Base.connection.execute(sql)

当我添加外键约束并在开发模式下启动应用程序时,数据库强制执行约束,如果用户有任何从属电话,我就无法删除用户。这是否适用于我是否在&#34; psql&#34; PostgreSQL控制台或IRB Rails控制台。

但是,当我尝试编写RSpec测试以查看外键是否强制执行删除限制时,测试失败并且我能够删除具有从属电话的用户。显然,&#34; rake db:test:prepare&#34;没有使用外键准备数据库。

我是否可以针对强制执行外键约束的数据库运行RSpec测试套件?

1 个答案:

答案 0 :(得分:3)

在Rails 4中,它很简单:

在config / application.rb中,设置

module YourApp
  class Application < Rails::Application
    config.active_record.schema_format = :sql
  end
end

rake RAILS_ENV=test db:migrate然后将您的架构转储到SQL db/structure.sqlrake RAILS_ENV=test test:load将加载它。这些任务使用pg_dump用于Postgres,因此它们将保留模式的每个方面。

Rails 3.2与测试数据库相关的Rake任务并不尊重config.active_record.schema_format,所以我甚至不打扰设置它。相反,我将任务改为使用db:structure:load而不是db:schema:load。在lib / tasks / db.rb中:

namespace :db do
  namespace :migrate do
    # We override the original of this task for consistency. Like the original, it doesn't seed.
    Rake::Task['db:migrate:reset'].clear
    task reset: [ 'db:drop', 'db:create', 'db:structure:load', 'db:migrate' ]
  end

  # This overrides the original, which does db:schema:load. The original doesn't migrate; this version does,
  # since, unlike schema.rb, *_structure.sql does not necessarily include all migrations.
  Rake::Task['db:setup'].clear
  task setup: [ 'test:ensure_environment_is_test', 'db:create', 'db:structure:load', 'db:migrate', 'db:seed' ]

  Rake::Task['db:reset'].clear
  task reset: [ 'test:ensure_environment_is_test', 'db:drop', 'db:setup' ]

  namespace :test do
    desc "rspec tasks depend on this task, so we override it to set up the database in the way that we want."
    Rake::Task['db:test:prepare'].clear
    task prepare: [ 'db:reset' ]
  end

end

namespace :test do
  task :ensure_environment_is_test do
    raise "Don't know how to db:setup RAILS_ENV=#{Rails.env}" unless Rails.env.test?
  end
end

手动运行rake db:structure:dump以创建db/structure.sql,并偶尔再次执行此操作以汇总迁移。您可以在每次迁移时修改上面的转储,就像Rails 4的任务一样,但您可能会或可能不会发现转储中的无关紧要的更改会让人烦恼。

早期版本的Rails需要更多的黑客攻击;希望我们不必去那里。

无论Rails版本如何,都值得阅读activerecord gem lib/active_record/railties/database.rb