Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stub file size on Active Storage test? (Test::Unit)

Tags:

I'm using Active Storage in a personal project. I want to check if the max size of files is being validated. I don't wanna use a real file, but I don't know how to stub an object.

Here's the test code:

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

Here's the validation on model:

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
like image 907
Matheus Richard Avatar asked Jul 30 '18 23:07

Matheus Richard


People also ask

How does active storage work in Rails?

Active Storage uses two tables in your application's database named active_storage_blobs and active_storage_attachments . After creating a new application (or upgrading your application to Rails 5.2), run rails active_storage:install to generate a migration that creates these tables.

What is Activestorage?

1 What is Active Storage? Active Storage facilitates uploading files to a cloud storage service like Amazon S3, Google Cloud Storage, or Microsoft Azure Storage and attaching those files to Active Record objects.


1 Answers

I read the code from your project, and I see that you've come up with an interesting solution, but here's another that matches your requirements.

Step 1 - Add mocha

Mocha is the de facto stubbing library for MiniTest fans, so add it to your Gemfile like so.

group :test do
  gem 'mocha'
end

Then run bundle install and require mocha in your test helper like so:

# 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

Step 2 - Create a dummy test file

Place a small file named 'test.png' in test/fixtures/files/. Note that my file is around 20KB in size, it's pretty small.

Step 3 - Add a unit test

Add the following to 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

Step 4 - Test inside the maximum filesize boundary

Let's make sure that a file of 20MB can be accepted by adding the following to test/models/page_test.rb:

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

Step 5 - Add a controller test for the happy path

Let's add a controller test for the happy path:

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

Step 6 - Add a controller test for the sad path

Time to test upload failure:

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

That's it. Hope it helps.

like image 131
stephenmurdoch Avatar answered Sep 28 '22 19:09

stephenmurdoch