ruby-on-rails - capybara 上传图片问题

标签 ruby-on-rails ruby testing rspec capybara

我需要使用 Capybara 和 Selenium 测试文件上传页面。

我写了这个测试:

require 'rails_helper'

describe 'Images', type: :feature do
  before(:each) do
    @user = create(:user)
    visit '/users/sign_in'
    fill_in 'sing-in-email-input', with: @user.email
    fill_in 'sign-in-password-input', with: @user.password
    click_button 'btn-sign-in'
    visit '/categories'
    click_on 'btn-new-category'
    expect(current_path) == new_category_path
    fill_in 'category_name', with: 'Test'
    click_button 'btn-create category'
    visit '/categories'
    first('.fa', :visible => false).click
    expect(current_path) == category_path(id: Category.last.slug.to_s)
  end

  it 'should allow a registered user to create image and go to it page', js: true do
    click_on 'btn-upload-images'
    expect(current_path) == new_image_path(id: Category.last.slug.to_s)
    attach_file('image[image]',
                File.join(Rails.root, '/spec/fixtures/solnce-kosmos-merkuriy.jpg'), :visible => false)
    click_on('btn-upload-img')
  end
end

我无法继续编写此测试并检查此图片是否已成功上传,因为在这一行:

click_on('btn-upload-img')

我遇到了这个错误:

Failure/Error: raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
     
     ActionController::RoutingError:
       No route matches [GET] "/home/Anton-S/RubymineProjects/rails_projects/gallery/spec/support/uploads/image/image/8790/solnce-kosmos-merkuriy.jpg"
     # /var/lib/gems/2.3.0/gems/railties-5.1.4/lib/rails/rack/logger.rb:36:in `call_app'
     # /var/lib/gems/2.3.0/gems/railties-5.1.4/lib/rails/rack/logger.rb:24:in `block in call'
     # /var/lib/gems/2.3.0/gems/railties-5.1.4/lib/rails/rack/logger.rb:24:in `call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/method_override.rb:22:in `call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/runtime.rb:22:in `call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/sendfile.rb:111:in `call'
     # /var/lib/gems/2.3.0/gems/railties-5.1.4/lib/rails/engine.rb:522:in `call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/urlmap.rb:68:in `block in call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/urlmap.rb:53:in `each'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/urlmap.rb:53:in `call'
     # /var/lib/gems/2.3.0/gems/capybara-2.16.1/lib/capybara/server.rb:44:in `call'
     # /var/lib/gems/2.3.0/gems/rack-2.0.3/lib/rack/handler/webrick.rb:86:in `service'
     # ------------------
     # --- Caused by: ---
     # Capybara::CapybaraError:
     #   Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true
     #   /var/lib/gems/2.3.0/gems/capybara-2.16.1/lib/capybara/session.rb:145:in `raise_server_error!'

Finished in 17.15 seconds (files took 2.55 seconds to load)
133 examples, 1 failure

Failed examples:

rspec ./spec/features/images_spec.rb:20 # Images should allow a registered user to create image and go to it page

我检查过我确实有这个文件

/spec/fixtures/solnce-kosmos-merkuriy.jpg

在正确的地方。

这是我的/config/initializers/carrierwave.rb

if Rails.env.test? || Rails.env.cucumber?
  CarrierWave.configure do |config|
    config.storage = :file
    config.enable_processing = false
  end

  # make sure our uploader is auto-loaded
  ImageUploader

  # use different dirs when testing
  CarrierWave::Uploader::Base.descendants.each do |klass|
    next if klass.anonymous?
    klass.class_eval do
      def cache_dir
        "#{Rails.root}/spec/support/uploads/tmp"
      end

      def store_dir
        "#{Rails.root}/spec/support/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
      end
    end
  end
end

我的rails_helper.rb

require 'support/factory_bot'
require 'spec_helper'
require 'shoulda/matchers'
require 'capybara/rspec'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'

ActiveRecord::Migration.maintain_test_schema!


module DeviseRequestSpecHelpers

  include Warden::Test::Helpers

  def sign_in(resource_or_scope, resource = nil)
    resource ||= resource_or_scope
    scope = Devise::Mapping.find_scope!(resource_or_scope)
    login_as(resource, scope: scope)
  end

  def sign_out(resource_or_scope)
    scope = Devise::Mapping.find_scope!(resource_or_scope)
    logout(scope)
  end

end

RSpec.configure do |config|
  Capybara.ignore_hidden_elements = false

  config.fixture_path = "#{::Rails.root}/spec/fixtures"

  config.use_transactional_fixtures = true

  config.infer_spec_type_from_file_location!

  config.filter_rails_from_backtrace!
  config.include Devise::Test::ControllerHelpers, type: :controller
  config.include DeviseRequestSpecHelpers, type: :request
end

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec

    with.library :rails
  end
end

我的spec_helper.rb

RSpec.configure do |config|
  config.expect_with :rspec do |expectations|
    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
  end

  config.mock_with :rspec do |mocks|
    mocks.verify_partial_doubles = true
  end

  config.shared_context_metadata_behavior = :apply_to_host_groups
end

此图片上传页面的 View 模板:

.row.main-new-image
  = form_for @image, url: create_image_path, html: {multipart: true, method: :post, remote: true } do |form|
    #new_image_error_explanation
    .field
      .p.text-center
        %span.btn.btn-new-image{id: 'new-image-button'}
          %p#p-new-image-button Choose image
          \#{form.file_field :image, id: 'new-image-id'}
    .field
      = form.hidden_field :user_id, value: current_user.id
    .actions
      .p.text-center
        = form.submit 'Upload', class: 'btn button-upload-img', id: 'btn-upload-img'

我所有其他的 Capybara 和 RSpec 测试都工作正常。

请帮助我了解此问题的原因以及如何解决和预防。

如果可以帮助解决这个问题,我可以提供更多信息。

UPDATE 1

阅读 Thomas Walpole 的评论后,我决定更新我的代码并尝试一些其他功能。 我包括新 gem

gem 'database_cleaner'

我还添加了新代码来清除测试上传的代码。 这里更新了 rails_helper.rb

require 'support/factory_bot'
require 'spec_helper'
require 'shoulda/matchers'
require 'capybara/rspec'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'

ActiveRecord::Migration.maintain_test_schema!
module DeviseRequestSpecHelpers

  include Warden::Test::Helpers

  def sign_in(resource_or_scope, resource = nil)
    resource ||= resource_or_scope
    scope = Devise::Mapping.find_scope!(resource_or_scope)
    login_as(resource, scope: scope)
  end

  def sign_out(resource_or_scope)
    scope = Devise::Mapping.find_scope!(resource_or_scope)
    logout(scope)
  end
end

RSpec.configure do |config|
  Capybara.javascript_driver = :selenium
  Capybara.ignore_hidden_elements = true
  Capybara.register_driver :selenium do |app|
    Capybara::Selenium::Driver.new(app, browser: :firefox)
  end

  config.include Capybara::DSL
  config.fixture_path = "#{::Rails.root}/spec/fixtures"
  config.use_transactional_fixtures = true
  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end
  
  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end
  
  config.after(:all) do
    if Rails.env.test?
      FileUtils.rm_rf(Dir["#{Rails.root}/spec/support/uploads"])
    end
  end
  config.infer_spec_type_from_file_location!
  config.filter_rails_from_backtrace!
  config.include Devise::Test::ControllerHelpers, type: :controller
  config.include DeviseRequestSpecHelpers, type: :request
end

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec

    with.library :rails
  end
end

这是我更新的测试

require 'rails_helper'

describe 'Images', type: :feature do
  before(:each) do
    @user = create(:user)
    visit '/users/sign_in'
    fill_in 'sing-in-email-input', with: @user.email
    fill_in 'sign-in-password-input', with: @user.password
    click_button 'btn-sign-in'
    visit '/categories'
    click_on 'btn-new-category'
    have_current_path(new_category_path)
    fill_in 'category_name', with: 'Test'
    click_button 'btn-create category'
    visit '/categories'
    first('.fa', :visible => false).click
    have_current_path(category_path(id: Category.last.slug.to_s))
  end

  #ISSUE WITH
  it 'should allow a registered user to create image and go to it page', :driver => :selenium do
    click_on 'btn-upload-images'
    have_current_path(new_image_path(id: Category.last.slug.to_s))
    attach_file('image[image]',
                File.join(Rails.root, '/spec/fixtures/solnce-kosmos-merkuriy.jpg'), :visible => false)
    click_on('btn-upload-img')
  end
end

正如 Thomas Walpole 告诉我的那样,我检查了测试日志并看到了下一个(关于当前测试):

Started GET "/en/categories/test/new_image" for 127.0.0.1 at 2018-01-30 14:33:26 +0200
Processing by ImagesController#new as HTML
  Parameters: {"locale"=>"en", "id"=>"test"}
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 22], ["LIMIT", 1]]
   (0.2ms)  SAVEPOINT active_record_2
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 22], ["LIMIT", 1]]
  SQL (0.3ms)  INSERT INTO "user_actions" ("user_id", "action_type", "url", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["user_id", 22], ["action_type", "navigation"], ["url", "http://127.0.0.1:38226/en/categories/test/new_image"], ["created_at", "2018-01-30 12:33:26.645909"], ["updated_at", "2018-01-30 12:33:26.645909"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_2
  Rendering images/new.html.haml within layouts/application
  Rendered images/_form.html.haml (5.2ms)
  Rendered images/new.html.haml within layouts/application (7.5ms)
  Category Load (1.1ms)  SELECT  DISTINCT categories.*, COUNT(images.*) AS images_count, categories.*, COUNT(likes.*) AS likes_count, categories.*, COUNT(comments.*) AS comments_count FROM "categories" LEFT OUTER JOIN "images" ON "images"."category_id" = "categories"."id" LEFT OUTER JOIN "images" "images_categories_join" ON "images_categories_join"."category_id" = "categories"."id" LEFT OUTER JOIN "likes" ON "likes"."image_id" = "images_categories_join"."id" LEFT OUTER JOIN "images" "images_categories_join_2" ON "images_categories_join_2"."category_id" = "categories"."id" LEFT OUTER JOIN "comments" ON "comments"."image_id" = "images_categories_join_2"."id" GROUP BY categories.id ORDER BY images_count DESC, likes_count DESC, comments_count DESC LIMIT $1  [["LIMIT", 5]]
  Rendered layouts/_header.html.haml (4.3ms)
  Rendered layouts/_footer.haml (0.0ms)
Completed 200 OK in 21ms (Views: 12.6ms | ActiveRecord: 2.1ms)
  Category Load (0.6ms)  SELECT  "categories".* FROM "categories" ORDER BY "categories"."id" DESC LIMIT $1  [["LIMIT", 1]]
Started POST "/en/categories/test/create_image" for 127.0.0.1 at 2018-01-30 14:33:26 +0200
Processing by ImagesController#create as JS
  Parameters: {"utf8"=>"✓", "image"=>{"image"=>#<ActionDispatch::Http::UploadedFile:0x0000000ae4e008 @tempfile=#<Tempfile:/tmp/RackMultipart20180130-28977-1nptliu.jpg>, @original_filename="solnce-kosmos-merkuriy.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"image[image]\"; filename=\"solnce-kosmos-merkuriy.jpg\"\r\nContent-Type: image/jpeg\r\n">, "user_id"=>"22"}, "commit"=>"Upload", "locale"=>"en", "id"=>"test"}
  Category Load (0.2ms)  SELECT  "categories".* FROM "categories" WHERE "categories"."slug" = $1 LIMIT $2  [["slug", "test"], ["LIMIT", 1]]
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 22], ["LIMIT", 1]]
   (0.3ms)  SAVEPOINT active_record_2
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 22], ["LIMIT", 1]]
  Category Load (0.2ms)  SELECT  "categories".* FROM "categories" WHERE "categories"."id" = $1 LIMIT $2  [["id", 18], ["LIMIT", 1]]
  SQL (0.4ms)  INSERT INTO "images" ("image", "category_id", "created_at", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["image", "solnce-kosmos-merkuriy.jpg"], ["category_id", 18], ["created_at", "2018-01-30 12:33:26.945577"], ["updated_at", "2018-01-30 12:33:26.945577"], ["user_id", 22]]
   (0.2ms)  RELEASE SAVEPOINT active_record_2
Redirected to http://127.0.0.1:38226/en/categories/test/12
Completed 200 OK in 22ms (ActiveRecord: 1.6ms)
Started GET "/en/categories/test/12" for 127.0.0.1 at 2018-01-30 14:33:26 +0200
Processing by ImagesController#show as HTML
  Parameters: {"locale"=>"en", "id"=>"test", "image_id"=>"12"}
  User Load (0.6ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 22], ["LIMIT", 1]]
   (0.1ms)  SAVEPOINT active_record_2
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 22], ["LIMIT", 1]]
  SQL (0.3ms)  INSERT INTO "user_actions" ("user_id", "action_type", "url", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"  [["user_id", 22], ["action_type", "navigation"], ["url", "http://127.0.0.1:38226/en/categories/test/12"], ["created_at", "2018-01-30 12:33:26.966397"], ["updated_at", "2018-01-30 12:33:26.966397"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_2
  Category Load (0.1ms)  SELECT  "categories".* FROM "categories" WHERE "categories"."slug" = $1 LIMIT $2  [["slug", "test"], ["LIMIT", 1]]
  Image Load (0.2ms)  SELECT  "images".* FROM "images" WHERE "images"."category_id" = $1 AND "images"."id" = $2 LIMIT $3  [["category_id", 18], ["id", 12], ["LIMIT", 1]]
  Like Load (0.2ms)  SELECT  "likes".* FROM "likes" WHERE "likes"."image_id" = $1 AND "likes"."user_id" = $2 LIMIT $3  [["image_id", 12], ["user_id", 22], ["LIMIT", 1]]
  Rendering images/show.html.haml within layouts/application
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 22], ["LIMIT", 1]]
   (0.3ms)  SELECT COUNT(*) FROM "comments" WHERE "comments"."image_id" = $1  [["image_id", 12]]
  Like Load (0.2ms)  SELECT  "likes".* FROM "likes" WHERE "likes"."user_id" = $1 AND "likes"."image_id" = 12 LIMIT $2  [["user_id", 22], ["LIMIT", 1]]
   (0.3ms)  SELECT COUNT(*) FROM "likes" WHERE "likes"."image_id" = $1  [["image_id", 12]]
  Rendered likes/_like.html.haml (3.2ms)
  Rendered likes/_like_form.html.haml (7.3ms)
  Rendered comments/_comments.html.haml (2.7ms)
  Rendered comments/_comments_container.html.haml (5.1ms)
  Rendered images/show.html.haml within layouts/application (25.6ms)
  Category Load (1.3ms)  SELECT  DISTINCT categories.*, COUNT(images.*) AS images_count, categories.*, COUNT(likes.*) AS likes_count, categories.*, COUNT(comments.*) AS comments_count FROM "categories" LEFT OUTER JOIN "images" ON "images"."category_id" = "categories"."id" LEFT OUTER JOIN "images" "images_categories_join" ON "images_categories_join"."category_id" = "categories"."id" LEFT OUTER JOIN "likes" ON "likes"."image_id" = "images_categories_join"."id" LEFT OUTER JOIN "images" "images_categories_join_2" ON "images_categories_join_2"."category_id" = "categories"."id" LEFT OUTER JOIN "comments" ON "comments"."image_id" = "images_categories_join_2"."id" GROUP BY categories.id ORDER BY images_count DESC, likes_count DESC, comments_count DESC LIMIT $1  [["LIMIT", 5]]
  Rendered layouts/_header.html.haml (4.6ms)
  Rendered layouts/_footer.haml (0.1ms)
Completed 200 OK in 43ms (Views: 30.0ms | ActiveRecord: 4.1ms)
Started GET "/home/Anton-S/RubymineProjects/rails_projects/gallery/spec/support/uploads/image/image/12/solnce-kosmos-merkuriy.jpg" for 127.0.0.1 at 2018-01-30 14:33:27 +0200
   (0.2ms)  ROLLBACK TO SAVEPOINT active_record_1
   (0.1ms)  ROLLBACK
   (0.1ms)  BEGIN
   (0.1ms)  COMMIT
   (0.0ms)  BEGIN
   (0.0ms)  SAVEPOINT active_record_1
   (0.1ms)  SAVEPOINT active_record_2

据我了解,图片已成功上传。 但是我仍然得到这个错误

Failure/Error: raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
     
     ActionController::RoutingError:
       No route matches [GET] "/home/Anton-S/RubymineProjects/rails_projects/gallery/spec/support/uploads/image/image/12/solnce-kosmos-merkuriy.jpg"

UPDATE 2

不幸的是,我找到的唯一解决方案是设置 /config/environments/test.rb

config.action_dispatch.show_exceptions = true

但我仍然认为有更好的解决方案。

最佳答案

因此,从日志中,我们可以看到文件正在成功上传。

问题是当显示页面呈现时,图像被添加到 HTML 中,其 URL 类似于 '/home/Anton-S/RubymineProjects/rails_projects/gallery/spec/support/uploads/image/image/12/solnce-kosmos-merkuriy.jpg' 这将不起作用,因为 Rails 应用程序不会响应应用程序根目录中 public 目录之外的随机文件。这样做将是一个巨大的安全问题。最简单的解决方案是将图像存储到 public 目录中,以便 Rails 应用程序将它们作为静态 Assets 提供。类似的东西

  def cache_dir
    "public/test/uploads/tmp"
  end

  def store_dir
    "public/test/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

注意:我还评论了您对 have_current_path 匹配器的使用。

关于ruby-on-rails - capybara 上传图片问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48499129/

相关文章:

java - 在 Ruby 中实现 Java 应用程序

ruby-on-rails - 如何在不实例化 Ruby 中的 A 对象的情况下确定类 A 是否继承自类 B?

ruby-on-rails - 用于站点内消息传递的 Ruby Gem? (想想 LinkedIn、Facebook 等)

PHP 5 : return by ref method produces unexpected results

unit-testing - Spring 引导配置文件。如何测试?

Android Espresso Intents 测试随机失败, `` init() 必须在使用此方法之前调用``

javascript - 以 simple_form 呈现动态选择列表

mysql - 使用 ruby​​ on Rails 中的联接获取列表中唯一的项目

sql - 如何按最近的帖子对用户进行排序?

ruby - Ubuntu bundler HTTPError