Rails 4:使用RSpec测试rake任务时的PG :: InFailedSqlTransaction

时间:2018-01-05 19:12:12

标签: ruby-on-rails rspec rake-task

我目前正在尝试使用RSpec测试rake任务。 我的Rails版本是4.2.4,而rspec-rails版本是3.3.2。

我在 rails_helper.rb 中有以下内容:

aClass

然后 spec / support / tasks.rb

ENV['RAILS_ENV'] ||= 'test'
require 'spec_helper'
require File.expand_path('../../config/environment', __FILE__)
require 'rspec/rails'
require 'capybara/rspec'

Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

RSpec.configure do |config|
  ...

  config.use_transactional_fixtures = false
  config.infer_spec_type_from_file_location!
  ...
end

我的 spec / support / database_cleaner.rb

require 'rake'

module TaskExampleGroup
  extend ActiveSupport::Concern

  included do
    let(:task_name) { self.class.top_level_description.sub(/\Arake /, "") }
    let(:tasks) { Rake::Task }

    # Make the Rake task available as `task` in your examples:
    subject(:task) { tasks[task_name] }
  end
end

RSpec.configure do |config|
  # Tag Rake specs with `:task` metadata or put them in the spec/tasks dir
  config.define_derived_metadata(file_path: %r{/spec/tasks/}) do |metadata|
    metadata[:type] = :task
  end

  config.include TaskExampleGroup, type: :task

  config.before(:suite) do
    Rails.application.load_tasks
  end
end

最后,规范:

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.append_after(:each) do
    DatabaseCleaner.clean
  end
end

问题是由于某种原因,执行此代码会导致以下错误:

require 'rails_helper'

describe "rake some:my_task", type: :task do

  # This test passes
  it 'preloads the Rails environment' do
    expect(task.prerequisites).to include 'environment'
  end

  # This test fails
  it 'creates AnotherModel' do
    my_hash = {foo => 'bar'}
    allow(MyClient::Event).to receive(:list).and_return(my_hash)

    expect { task.execute }.not_to raise_error

    expect(AnotherModel.count).to eq(1)
  end
end

rake任务看起来像这样:

Failure/Error: AnotherModel.count
ActiveRecord::StatementInvalid:
  PG::InFailedSqlTransaction: ERROR:  current transaction is aborted, commands ignored until end of transaction block

我尝试过跑步:

namespace :some do desc 'Parse stream' task my_task: :environment do |_t| cint_events['events'].each do |event| begin events = MyClient::Event.list(some_hash) events.each do |event| if some_condition # The test should check whether this object gets created AnotherModel.first_or_create_by(foo: some_hash['foo']) end end rescue => e # Log errors end end end end

然后再次运行规范,但我不断收到上述错误。这可能是由什么引起的?

提前致谢!

2 个答案:

答案 0 :(得分:0)

您将测试配置为在数据库事务中运行,这是一件好事。 但是在你的rake任务中你只会吃掉所有出现的错误:

rescue => e
  # Log errors
end

但是,某些错误仍可能导致事务失败和回滚。所以我的猜测是,第一次调用数据库时会发生一些严重的错误(例如,数据库不知道列foo)。之后,它会捕获错误,并且您正在向已经中止的事务添加一个语句(AnotherModel.count),该事务失败。

这是一个好的起点,就是检查救援区中e.message的价值

另请注意: 盲目地拯救所有错误绝不是一个好主意,而且几乎总会导致奇怪和意外的行为。

答案 1 :(得分:0)

当在测试环境中并且通过 ActiveRecord 的 SQL 查询无法识别查询中的字段时,似乎会发生此错误。换句话说,您有一个范围,或者正在尝试返回一些带有错误数据库列名的 ActiveRecord 关系。

请参阅此相关帖子: ActiveRecord::StatementInvalid: PG InFailedSqlTransaction