为什么我的Capybara / Rspec测试中的db记录会出现ElementNotFound错误?

时间:2013-10-17 22:44:24

标签: ruby-on-rails activerecord rspec ruby-on-rails-4 capybara

我是Rails的新手,我正在尝试为提交表单编写测试。它应该测试表单是否可以为Deal模型创建新记录。它失败了,因为我试图创建的社区对象实际上并没有出现在下拉列表中。我想知道是否由于某种原因没有创建记录,或者没有坚持。

我得到的错误是

Failure/Error: select community.name,   :from => "deal[community_id]"
Capybara::ElementNotFound:
Unable to find option "Community7"

以下是我的规范的相关部分:

describe "Deal Pages" do

  subject {page}

  describe "create Deal" do
    let(:user) { FactoryGirl.create :user, username: "user_1", email: "user_1@email.com", password: "password" }
    let(:community) { FactoryGirl.create :community, name: "Community7", title: "Community 7", description: "A great community", user: user }

    before(:each) do
      sign_in user
      visit new_deal_path
    end

    let(:submit) { "Submit Deal" }

    describe "with invalid information" do

      it "should not create a Deal" do
        expect { click_button submit }.not_to change(Deal, :count)
      end

      describe "after submission" do
        before { click_button submit }

        it { should have_title('Submit A New Deal') }
        it { should have_content('error') }
      end
    end

    describe "with valid information" do

      it "should create a Deal" do
        fill_in "Title",         with: "A New Deal"
        select community.name,  :from => "deal[community_id]"
        fill_in "Url",        with: "http://www.amazon.com"
        fill_in "Description",     with: "This is the best Deal!"

        expect { click_button submit }.to change(Deal, :count).by(1)
      end
    end
  end

我正在使用database_cleaner gem,我知道配置它会在每个示例后清理db记录。无论出于何种原因,这件事只是给了我很多麻烦。

spec_helper.rb

require 'rubygems'
require 'spork'

Spork.prefork do
  ENV["RAILS_ENV"] ||= 'test'
  require File.expand_path("../../config/environment", __FILE__)
  require 'rspec/rails'
  require 'rspec/autorun'

  # Requires supporting ruby files with custom matchers and macros, etc,
  # in spec/support/ and its subdirectories.
  Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

  # Checks for pending migrations before tests are run.
  # If you are not using ActiveRecord, you can remove this line.
  ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)

  RSpec.configure do |config|
    # ## Mock Framework
    #
    # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
    #
    # config.mock_with :mocha
    # config.mock_with :flexmock
    # config.mock_with :rr

# Devise support
#config.include Devise::TestHelpers, :type => :controller

config.before(:suite) do
  DatabaseCleaner.strategy = :truncation
end

config.before(:each) do
  DatabaseCleaner.start
  Rails.logger.debug "DB Clean start"
end

config.after(:each) do
  DatabaseCleaner.clean
  Rails.logger.debug "DB Clean clean"
end

# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"

# 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.

#for database_cleaner
config.use_transactional_fixtures = false

# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false

# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
#     --seed 1234
config.order = "random"
# Include the Capybara DSL so that specs in spec/requests still work.
config.include Capybara::DSL
# Disable the old-style object.should syntax.
config.expect_with :rspec do |c|
  c.syntax = :expect
end


 end
end

Spork.each_run do
  # This code will be run each time you run your specs.

end

factories.rb

FactoryGirl.define do
  factory :user do
    sequence(:username)  { |n| "Person_#{n}" }
    sequence(:email) { |n| "person_#{n}@example.com" }
    password "password"
    password_confirmation "password"
  end

  factory :community do
    sequence(:name)  { |n| "Community#{n}" }
    sequence(:title) { |n| "Community #{n} Title" }
    sequence(:description) { |n| "a great community #{n}" }
    user
  end

  factory :deal do
    sequence(:title) { |n| "Great Deal #{n}" }
    url "http://www.amazon.com"
    sequence(:description) { |n| "deal #{n} rocks so click it" }
    user
    community
  end
end

2 个答案:

答案 0 :(得分:2)

好的,我想了一会儿,因为我讨厌通过谷歌找到未回答的问题时,我会跟进。

问题是我在父母描述块的顶部有以下内容:

let(:community) { FactoryGirl.create :community, name: "Community7", title: "Community 7", description: "A great community", user: user }

除了不需要指定单个属性之外,问题在于当与database_cleaner配置结合使用时,它会在每个示例之后转储数据库。在“创建交易”块之前有一个或多个示例,因此在调用community.name时,社区表中没有数据。我把我的规格改为如此,我觉得我更容易处理和遵循:

describe "create Deal" do

    before(:each) do
        @user = FactoryGirl.create :user
        @community = FactoryGirl.create :community
        sign_in @user
        visit new_deal_path
    end

    let(:submit) { "Submit Deal" }

    describe "with invalid information" do

        it "should not create a Deal" do
            expect { click_button submit }.not_to change(Deal, :count)
        end

        describe "after submission" do
            before { click_button submit }

            it { should have_title('Submit A New Deal') }
            it { should have_content('error') }
        end
    end 

    describe "with valid information" do

        it "should create a Deal" do
            fill_in "Title",         with: "A New Deal"
            select @community.name, :from => "deal[community_id]"
            fill_in "Url",        with: "http://www.amazon.com"
            fill_in "Description",     with: "This is the best Deal!"

            expect { click_button submit }.to change(Deal, :count).by(1)
        end
    end
end

before(:each)确保在每个子示例运行之前我获得一个新的用户和社区对象。希望这有助于某人,但如果不是这对我来说是一次学习经历。

答案 1 :(得分:1)

问题是您在

中传递了“Community7”
let(:community) { FactoryGirl.create :community, name: "Community7", title: "Community 7", description: "A great community", user: user }

虽然“社区”一词也是通过工厂添加的:社区:

factory :community do
    sequence(:name)  { |n| "Community#{n}" }
    sequence(:title) { |n| "Community #{n} Title" }
    ...
end

结果你得到了“CommunityCommunity7”和相应的错误:

Unable to find option "Community7"

我会将工厂调整为

factory :community do
    sequence(:name)  { |n| "#{n}" }
    sequence(:title) { |n| "#{n} Title" }
    ...
end