【问题标题】:How to stub file size on Active Storage test? (Test::Unit)如何在 Active Storage 测试中存根文件大小? (测试::单元)
【发布时间】:2019-01-07 06:14:47
【问题描述】:

我在 personal project 中使用 Active Storage。我想检查是否正在验证文件的最大大小。我不想使用真实文件,但我不知道如何存根对象。

这是测试代码:

test "should not upload file bigger than max size allowed" do
  refute @page.file.attached?

  patch "/#{@page.url}", params: {
    page: {
      url: "/#{@page.url}",
      file: my_stub_file_with_big_size
    }
  }
  assert_response :not_acceptable
  @page.reload

  refute  @page.file.attached?
end

这是对模型的验证:

def file_size
  if file.attached? && file.byte_size > MAX_FILE_SIZE
    file.purge
    errors.add(:file, "File is too big. Max size is 20mb.")
  end
end

【问题讨论】:

    标签: ruby-on-rails file testing mocking rails-activestorage


    【解决方案1】:

    我从您的项目中阅读了代码,发现您提出了一个有趣的解决方案,但这里有另一个符合您要求的解决方案。

    第 1 步 - 添加摩卡咖啡

    Mocha 是 MiniTest 粉丝事实上的存根库,所以像这样将它添加到您的 Gemfile 中。

    group :test do
      gem 'mocha'
    end
    

    然后运行bundle install 并在您的测试助手中要求 mocha,如下所示:

    # test/test_helper.rb
    
    ENV['RAILS_ENV'] ||= 'test'
    
    require_relative '../config/environment'
    require 'rails/test_help'
    
    # add this line
    require 'mocha/minitest'
    
    class ActiveSupport::TestCase    
      # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
      fixtures :all    
    end
    

    第 2 步 - 创建一个虚拟测试文件

    test/fixtures/files/ 中放置一个名为“test.png”的小文件。请注意,我的文件大小约为 20KB,非常小。

    第 3 步 - 添加单元测试

    将以下内容添加到test/models/page_test.rb

    test 'file has max size' do
      # get the path of the file
      fixture_path = File.join(Rails.root, 'test', 'fixtures', 'files', 'test.png')
    
      # open the file  
      fixture_file = File.open(fixture_path)
    
      # stub the file size to 21 MB
      fixture_file.stubs(:size).returns(21.megabytes)
    
      # attach the file to your @page instance
      @page.file.attach(io: fixture_file, filename: 'test.png')
    
      # the record should now be invalid
      refute @page.valid?
    
      # it should not have an attachment
      refute @page.file.attached?
    
      # assert the error message matches a regex 
      assert_match /is too big/, @page.errors[:file].to_s
    end
    

    第 4 步 - 在最大文件大小边界内测试

    让我们通过将以下内容添加到test/models/page_test.rb 来确保可以接受 20MB 的文件:

    test 'file with valid size' do
      # get the path of the file
      fixture_path = File.join(Rails.root, 'test', 'fixtures', 'files', 'test.png')
    
      # open the file  
      fixture_file = File.open(fixture_path)
    
      # stub the file size to 20 MB
      fixture_file.stubs(:size).returns(20.megabytes)
    
      # attach the file to your @page instance
      @page.file.attach(io: fixture_file, filename: 'test.png')
    
      # the record should now be valid
      assert @page.valid?
    
      # it should have an attachment
      assert @page.file.attached?
    
      # probably unnecessary, but let's test that there are no errors on @page
      assert_empty @page.errors[:file]
    end
    

    第 5 步 - 为快乐路径添加控制器测试

    让我们为快乐路径添加一个控制器测试:

    test 'should update page' do
      # fixture_file_upload knows where the fixtures folder is
      # and makes sure we have a correctly signed file
      fixture_file = fixture_file_upload('files/test.png', 'image/png')
    
      # we need to stub the byte_size of any instance of ActiveStorage::Blob
      # as that is what our validations are going to be run against 
      ActiveStorage::Blob.any_instance.stubs(:byte_size).returns(20.megabytes)
    
      # make sure our ActiveStorage records are created
      assert_difference ['ActiveStorage::Blob.count', 'ActiveStorage::Attachment.count'] do
        # add our fixture file into the params
        patch page_url, params: { page: { file: fixture_file } }
      end
    
      # make sure we are redirected to the right place 
      assert_redirected_to page_url
    end
    

    第 6 步 - 为悲伤路径添加控制器测试

    测试上传失败的时间:

    test 'should render edit template when update fails' do
      # like before, we get correctly formatted test file by calling
      # fixture_file_upload method and passing in the test file
      fixture_file = fixture_file_upload('files/test.png', 'image/png')
    
      # stub the byte_size to be 21MB
      ActiveStorage::Blob.any_instance.stubs(:byte_size).returns(21.megabytes)
    
      # make sure zero ActiveStorage records are created
      assert_no_difference ['ActiveStorage::Blob.count', 'ActiveStorage::Attachment.count'] do
        # send the request
        patch page_url, params: { page: { file: fixture_file } }
      end
    
      # check the response body for an error message
      assert_match /is too big/, response.body.to_s
    end
    

    就是这样。希望对您有所帮助。

    【讨论】:

    • 哇,这正是我想要的!谢谢你的详细回答。由于它是如此完整,我认为您可以打开一个拉取请求添加它。你怎么看?
    【解决方案2】:

    我更喜欢在单独的类中处理给定模型上的任何自定义验证,因此我的方法可能不适合您。有关详细信息,请参阅 Rails 文档here

    在我的自定义验证类测试中,我使用 Rspec 的 receive_message_chain 来存根对 ActiveStorage 映像的任何调用。对于有问题的模型,我有一个定义了with_image 特征的工厂(感谢this related answer),然后我对模型的image 进行了必要的调用。不幸的是,这需要您的测试套件中至少有一个图像夹具,但它可以是您想要的任何大小。完整示例如下:

    user.rb (model) - 注意自定义 target 选项的使用

    class User < ApplicationRecord
      has_one_attached :image
    
      validates_with ImageValidator, target: :image
    end
    

    user.rb(工厂)

    FactoryBot.define do
      factory :user do
        trait :with_image do
          after(:build) do |user|
            user.image.attach(io: File.open(Rails.root.join('spec', 'fixtures', 'user-image.png')), filename: 'user-image.png', content_type: 'image/png')
          end
        end
      end
    end
    

    image_validator.rb(验证类)- 然后使用自定义 target 选项来确定应使用该类验证给定模型上的哪个属性。

    class ImageValidator < ActiveModel::Validator
      def validate(record)
        if options[:target].present? && record.send(target).attached?
          if record.send(target).blob.byte_size > 1000000
            record.send(target).purge
            record.errors.add(:image, "Image too large")
          elsif !record.send(target).blob.content_type.starts_with?('image/')
            record.send(target).purge
            record.errors.add(:image, "Incorrect image format")
          end
        end
      end
    
      private 
    
      def target
        options[:target].to_s
      end
    end
    

    image_validator_spec.rb(测试)

    require "rails_helper"
    
    describe ImageValidator do
      subject { build(:user, :with_image) }
    
      context "when an Image is too large" do
        before do
          allow(subject.image).
            to receive_message_chain(:blob, :byte_size).and_return(1000001)
        end
    
        it "should be invalid" do
          expect(subject).to be_invalid
        end
      end
    end
    

    【讨论】:

      猜你喜欢
      • 2017-09-11
      • 1970-01-01
      • 2014-05-15
      • 1970-01-01
      • 2012-12-25
      • 2012-04-12
      • 2011-05-07
      • 2017-07-29
      • 1970-01-01
      相关资源
      最近更新 更多