【问题标题】:ruby on rails - fixture_file_upload not working with controller tests for CarrierWaveruby on rails - fixture_file_upload 不适用于 CarrierWave 的控制器测试
【发布时间】:2018-09-25 01:13:33
【问题描述】:

我在使用 CarrierWave 和 Minitest 对我的 ROR 5.2 项目进行控制器集成测试时遇到问题。我正在使用 fixture_file_upload 来“上传”一个文件,该文件在我的模型测试中正常工作,但由于我的模型中 CarrierWave 属性的存在验证而在我的控制器集成测试中失败。它总是在创建操作上失败。即使我没有更新 CarrierWave 属性,更新操作也会偶尔随机失败。

我使用 byebug 来检查 create 操作的属性值,但它什么也没返回;该属性从未设置。我还检查了新创建的模型的错误,它们是:“图标不能为空”。

fixture_file_upload 在我的模型测试中运行良好,手动创建/更新/上传(不在测试中)也运行良好。

我已经在 Google 上搜索了好几个小时,试图找出我做错了什么,但我发现所有内容都说要使用 fixture_file_upload,但我没有找到与我遇到的问题相关的任何内容。

CarrierWave 初始化器:

CarrierWave.configure do |config|
    #To let CarrierWave work on heroku
    config.root = Rails.root.join('tmp')
    config.cache_dir = 'uploads/tmp'

    if Rails.env.test? || Rails.env.development?
        config.storage = :file

        #config for tests is done in test/test_helper.rb
    else
        config.storage = :fog

        config.fog_credentials = { #Configuration for Amazon S3
            provider: 'AWS',
            aws_access_key_id: Rails.application.credentials.aws[:access_key_id],
            aws_secret_access_key: Rails.application.credentials.aws[:secret_access_key],
            region: Rails.application.credentials.aws[:region]
        }

        config.fog_public = false
        config.fog_directory = Rails.application.credentials.aws[:bucket_name]
        config.fog_host = "#{Rails.application.credentials.aws[:asset_url]}/#{Rails.application.credentials.aws[:bucket_name]}"
    end
end

测试助手:

ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
require 'rails/test_help'
include ActionDispatch::TestProcess #for fixture_file_upload

module UsersHelper
    def login_as(user)
        get login_user_url
        assert_response :success

        post user_login_url(session: { username: user.username, password: 'test1234' }) #have to hard code password here since passwords are stored encrypted
        assert_redirected_to root_url, 'Login did not redirect'
    end

    def logout
        get user_logout
    end
end

class ActiveSupport::TestCase
    # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
    fixtures :all
    # Add more helper methods to be used by all tests here...
end

class ActionDispatch::IntegrationTest
    include UsersHelper
end

#setup carrierwave for tests
carrierwave_root = Rails.root.join('tmp', 'test', 'support', 'carrierwave')
carrierwave_template = Rails.root.join('test', 'fixtures', 'files')

CarrierWave.configure do |config|
    config.root = carrierwave_root
    config.cache_dir = carrierwave_root.join('carrierwave_cache')
    config.enable_processing = false
end

#copy carrierwave fixture files to carrierwave root
puts 'Copying CarrierWave fixture files..'
puts carrierwave_template.join('uploads')
puts carrierwave_root
FileUtils.cp_r carrierwave_template.join('uploads'), carrierwave_root

Minitest.after_run do
    #remove carrierwave files
    puts 'Deleting CarrerWave fixture files...'

    Dir.glob(Pathname.new(carrierwave_root).join('*')).each do |dir|
        FileUtils.remove_entry(dir)
    end

    puts 'Cleaning CarrierWave cached files...'
    CarrierWave.clean_cached_files!(0)
end

型号:

class Category < ApplicationRecord
    mount_uploader :icon, IconUploader, dependent: :destroy

    validates :name, length: { minimum: 2, maximum: 30 }, uniqueness: { case_sensitive: false }
    validates :slug, length: { minimum: 2, maximum: 30 }, uniqueness: { case_sensitive: false }
    validates :icon, presence: true
end

图标上传器:

class IconUploader < CarrierWave::Uploader::Base
    include CarrierWave::MiniMagick

    after :remove, :delete_empty_upstream_dirs

    def store_dir
        "#{base_store_dir}/#{model.id}"
    end

    def base_store_dir
        "uploads/#{model.class.to_s.underscore}/#{mounted_as}"
    end

    #override file name, for uniqueness
    def filename
        random_token = SecureRandom.hex(6/2) #length of 6 characters
        token_var = "@#{mounted_as}_secure_token" #get token variable name
        token = model.instance_variable_get(token_var) #get token from token variable name
        token ||= model.instance_variable_set(token_var, random_token) #if token isn't already set, set it

        @name ||= "#{token}_#{super}" if original_filename.present? and super.present? #create name, using instance variable so token isn't changed (function is called multiple times)
    end

    #set size limits
    def size_range
        1.kilobyte..256.kilobytes #1 kilobyte to 256 kilobytes
    end

    #resize image if width or height is greater than 256px, add padding if needed
    process resize_and_pad: [256, 256] #don't use resize_to_fit, as it adds a white background at least to SVG images

    # Add a white list of extensions which are allowed to be uploaded.
    # For images you might use something like this:
    def extension_whitelist
        %w(jpg jpeg png svg)
    end

    #whitelist of content types
    def content_type_whitelist
        /image\// #whitelist images
    end

    private

    #delete directory if it's empty
    def delete_empty_upstream_dirs
        path = ::File.expand_path(store_dir, root)
        Dir.delete(path) #fails if path not empty dir

        path = ::File.expand_path(base_store_dir, root)
        Dir.delete(path) #fails if path not empty dir
    rescue SystemCallError => e
        Rails.logger.error(e.message) #log error

        true #nothing, the dir is not empty
    end
end

控制器创建动作:

def create
    data = params.require(:category).permit([ :name, :icon, :icon_cache ])

    @category = Category.new(data)

    if @category.save
        flash.notice = 'Category successfully created.'
        redirect_to categories_path
    else
        render :add #show errors
    end
end

控制器测试:

test "should post category_create when admin" do
    login_as(users(:admin))

    get add_category_url
    assert_response :success

    icon = fixture_file_upload(Rails.root.join('test', 'fixtures', 'files', 'category_icon.svg'))

    #fails: validation error: "Icon can't be blank"
    post category_create_url(category: { name: 'test901', icon: icon, icon_cache: '' })
    assert_redirected_to categories_url
    assert_equal 'Category successfully created.', flash[:notice]
end

模型测试:

test "should save when all details correct" do
    category = Category.new(name: 'tools',
            icon: fixture_file_upload(Rails.root.join('test', 'fixtures', 'files', 'category_icon.svg')))

    #succeeds
    assert category.save, 'Not saved when all details correct: ' + category.errors.full_messages.to_s
end

【问题讨论】:

  • 您找到解决方案了吗?我整天都在处理同样的问题。
  • ruby 可选的括号以获得胜利。 ??????????‍♂️

标签: ruby-on-rails ruby-on-rails-5 carrierwave minitest


【解决方案1】:
post category_create_url(category: { name: 'test901', icon: icon, icon_cache: '' })

应该是

post category_create_url, params: {category: { name: 'test901', icon: icon, icon_cache: '' }}

第一个是将参数发送到 route_helper 并导致尝试通过无法工作的查询字符串参数传递文件。

第二个将参数发送到 post 方法,该方法正确地将参数作为多部分/表单数据发布,这将正确地将文件对象发布到控制器。

【讨论】:

  • 解决了,谢谢!注意:fixture_file_upload 不会推断 MIME 类型,因此如果您使用 content_type_whitelist,则需要将 MIME 类型作为第二个参数添加到 fixture_file_upload,如下所示:fixture_file_upload 'path/to/file', 'mime/type'
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多