When running capybara feature specs I can see lots of Slow factory notices which are populated by factory_girl. These Slow factory things heavy slow down the feature specs, I think, even feature specs are intrinsic slow specs. Then I had some inspect and found out most of the Slow factory was caused by paperclip. We had model using paperclip here:
FactoryGirl.define do
factory :asset do
image Rails.root.join('spec/fixtures/sample.jpg').open
end
end
So I wonder if there's a way like test mode for paperclip to speed up tests. I have simple solution here: Just copy the original file instead of actually crop it.
You can set the paperclip image fields in your factory, which will cause paperclip to not even attempt to process the image:
factory :asset do
# Set the image fields manually to avoid uploading / processing the image
image_file_name { 'test.jpg' }
image_content_type { 'image/jpeg' }
image_file_size { 256 }
end
I found the way out to achieve this, see this code:
FactoryGirl.define do
factory :asset do
image_file_name { 'sample.jpg' }
image_content_type 'image/jpeg'
image_file_size 256
after(:create) do |asset|
image_file = Rails.root.join("spec/fixtures/#{asset.image_file_name}")
# cp test image to direcotries
[:original, :medium, :thumb].each do |size|
dest_path = asset.image.path(size)
`mkdir -p #{File.dirname(dest_path)}`
`cp #{image_file} #{dest_path}`
end
end
end
end
Manually cp the test image to the real asset image paths in a factory_girl after create hook. It works like a charm.
Related
Just trying to add some basic tests to my Carrierwave Uploader. I am starting with the default generated tests and trying to go from there. I am getting a weird error and not sure where to go from here.
I have a polymorphic Upload model which has:
mount_uploader :file, DocumentUploader
My DocumentUploader has something like this:
def store_dir
"#{model.uploadable_type.downcase.pluralize.underscore}/#{model.parent_asset.id}/uploads/#{model.id}/"
end
This makes my store directories look like:
/locations/24/uploads/56
when I run the default tests for example:
test "should destroy upload" do
assert_difference('Upload.count', -1) do
delete :destroy, id: #upload
end
assert_redirected_to uploads_path
end
I get:
ERROR["test_should_destroy_upload", UploadsControllerTest,1.9893769259997498]
test_should_destroy_upload#UploadsControllerTest (1.99s)
NoMethodError: NoMethodError: undefined method `id' for nil:NilClass
app/uploaders/document_uploader.rb:34:in `store_dir'
In my Uploads fixtures I have the polymorphic associates set etc. Themodel is not being set to the Uploadable association in the DocumentUploader i.e. it's nil.
All my other tests work so far and my uploader works fine in production and development etc. I am sure I am missing something trivial in the set-up or it's a Carrierwave specific issue.
Chiming in really late here.
Using fog with carrierwave I have found to be a drag. carrierwave-aws gem is non-negotiable for me.
The Read Me provides a quick, overall stub to add to the uploads.rb configuration file:
config.aws_credentials = {
[...], # Required
stub_responses: Rails.env.test? # Optional, avoid hitting S3 actual during tests
}
For the presentation layer, I find this acceptable because, you often want to see results, and particularly the versions of such results. Once those run properly, they are hard to break if the method does not change - and those are rather static methods.
I wrote an API which can return latest 5 Newsletter and its image, but I am stuck at writing its rspec test.
First of all, here is the relationship between model.
Newsletter has_many NewsletterImages
NewsletterImage belong_to Newsletter
Secondly, I thought that I need to create some data in test database, so I wrote following code in rspec file.
7.times do |i|
n = Newsletter.create(title: "Test#{i}", content: "TestContents#{i}")
2.times do |i|
ni = NewsletterImage.create(newsletter_id: n.id, order: i)
ni.image = File.open('xxx.png')
ni.save
end
end
So, I need to upload file in very test? Is there a better way to generate data and test?
Better to use Factory Girl to make your test data. That way, you can write clean tests like
# /spec/factories/newsletter_factory.rb
FactoryGirl.define do
factory :newsletter do
title "My newsletter"
content "Some content"
end
end
# /spec/factories/newsletter_image_factory.rb
FactoryGirl.define do
factory :newsletter_image do
newsletter
image fixture_file_upload( Rails.root + 'spec/fixtures/images/example.jpg', "image/jpg")
end
end
# spec/models/newsletter_spec.rb
image = create :newsletter_image
expect(image.newsletter.title).to eq 'My Newsletter'
With all of the details of how the models are created hidden in the factory definition files, it's then easy to share the code across many tests.
For more detail about adding carrierwave files to Factory Girl definitions, look for other answers such as this one: https://stackoverflow.com/a/9952914/693349
Does anyone know the proper way to create PaperClip 4.0 attachments with factory_girl, bypassing any of the PaperClip processing and validation?
I used to just be able to do the following in my factory:
factory :attachment do
supporting_documentation_file_name { 'test.pdf' }
supporting_documentation_content_type { 'application/pdf' }
supporting_documentation_file_size { 1024 }
# ...
end
This would basically trick PaperClip into thinking that there was a valid attachment.
After upgrading from 3.5.3 to 4.0, I now get a validation error:
ActiveRecord::RecordInvalid: Validation failed: Image translation missing: en.activerecord.errors.models.attachment.attributes.supporting_documentation.spoofed_media_type
NOTE: The original discussion for PaperClip 3.X is here: How Do I Use Factory Girl To Generate A Paperclip Attachment?
The issue appears to be caused by line 61 in media_type_spoof_detector.
Paperclip is trying to find the mime type of the "file" you have uploaded. When there isn't one, it's failing validation to protect you from file type spoofing.
I haven't tried this myself, but perhaps your best bet would be to use a real file, and set it using the fixture_file_upload method from ActionDispatch::TestProcess.
factory :attachment do
supporting_documentation { fixture_file_upload 'test.pdf', 'application/pdf' }
# This is to prevent Errno::EMFILE: Too many open files
after_create do |attachment, proxy|
proxy.supporting_documentation.close
end
end
You will need to include ActionDispatch::TestProcess in test_helper.rb
This was first posted here.
I am currently trying to populate a development database on a project with a bunch of fake data, to simulate how it will look and operate with hundreds of articles / users. I looked into different gems to do the task - such as Factory Girl, but documentation was very lacking and I didn't get it - but ended up using the Populator and Faker gems and did the following rake task...
namespace :db do
desc "Testing populator"
task :populate => :environment do
require "populator"
require "faker"
User.populate 3 do |user|
name = Faker::Internet.user_name
user.name = name
user.cached_slug = name
user.email = Faker::Internet.email
user.created_at = 4.years.ago..Time.now
end
end
end
Works great...for text based data. However, all users have an avatar that can be uploaded via Paperclip attachment, as well as all regular content have thumbnail attachments in the same manner.
I understand the Populator gem simply does a straight population to the database and not necessarily running through ActiveRecord validations to do so..therefor I would assume Paperclip can't run to generate all the different thumbnails and needed (and uploaded to the proper directory) for the avatar if I just filled the field with a filepath in the rake task above.
Is there a way to populate fake images, via Populator or another way? Or perhaps a way to point the rake task at a directory of stock images on my hard drive to autogenerate random thumbnails for each record? Took a hunt on Google for a way, but have not turned up much information on the subject.
UPDATE
The final solution, based on pwnfactory's line of thinking...
namespace :db do
desc "Testing populator"
task :populate => :environment do
require "populator"
require "faker"
User.populate 3 do |user|
name = Faker::Internet.user_name
user.name = name
user.cached_slug = name
user.email = Faker::Internet.email
user.created_at = 4.years.ago..Time.now
end
User.all.each { |user| user.avatar = File.open(Dir.glob(File.join(Rails.root, 'sampleimages', '*')).sample); user.save! }
end
end
It basically loops back around and uploaded avatars from the sampleimages directory on all the records.
To associate a random image in your task, you could try the following:
user.avatar = File.open(Dir.glob(File.join(Rails.root, 'sampleimages', '*')).sample)
where sampleimages is a directory containing avatars to be associated at random
user.avatar = File.open(Dir['app/assets/images/*.jpg'].sample)
One way I get around this is to put a conditional in my views.
Let's say your model is "user", and it has an avatar. Then you can do the following:
<% if product.avatar.exists? %>
... show the attached photo
<% else %>
.. show a default photo
<% end %>
This works for me with Paperclip, and I use it in my dev database all the time rather than worrying about having all the image attached to all the users.
Total Rspec noob here. Writing my first tests tonight.
I've got a model called Image. Using paperclip I attach a file called photo. Standard stuff. I've run the paperclip generator and everything works fine in production and test modes.
Now I have a spec file called image.rb and it looks like this (it was created by ryanb's nifty_scaffold generator):
require File.dirname(__FILE__) + '/../spec_helper'
describe Image do
it "should be valid" do
Image.new.should be_valid
end
end
This test fails and I realise that it's because of my model validations (i.e. validates_attachment_presence)
The error that I get is:
Errors: Photo file name must be set., Photo file size file size must be between 0 and 1048576 bytes., Photo content type is not included in the list
So how do I tell rspec to upload a photo when it runs my test?
I'm guessing that it's got somethign to do with fixtures.... maybe not though. I've tried playing around with them but not having any luck. For the record, I've created a folder called images inside my fixtures folder and the two files I want to use in my tests are called rails.png and grid.png)
I've tried doing the following:
it "should be valid" do
image = Image.new :photo => fixture_file_upload('images/rails.png', 'image/png').should be_valid
# I've also tried adding stuff like this
#image.stub!(:has_attached_file).with(:photo).and_return( true )
#image.stub!(:save_attached_files).and_return true
#image.save.should be_true
end
But rspec complains about "fixture_file_upload" not being recognised... I am planning to get that Rspec book. And I've trawled around the net for an answer but can't seem to find anything. My test database DOES get populated with some data when I remove the validations from my model so I know that some of it works ok.
Thanks in advance,
EDIT:
images.yml looks like this:
one:
name: MyString
description: MyString
two:
name: MyString
description: MyString
This should work with Rails 2.X:
Image.new :photo => File.new(RAILS_ROOT + '/spec/fixtures/images/rails.png')
As of Rails 3, RAILS_ROOT is no longer used, instead you should use Rails.root.
This should work with Rails 3:
Image.new :photo => File.new(Rails.root + 'spec/fixtures/images/rails.png')
Definitely get the RSpec book, it's fantastic.
Rails.root is a pathname object so you can use it like this:
Image.new :photo => Rails.root.join("spec/fixtures/images/rails.png").open
Edit - probably does not work in Rails 3...
see answer by #Paul Rosania
In case anyone else finds this via Google, RAILS_ROOT is no longer valid in Rails 3.0. That line should read:
Image.new :photo => File.new(Rails.root + 'spec/fixtures/images/rails.png')
(Note the lack of leading slash!)
I use the multipart_body gem in my integration tests. Its a bit truer to BDD than testing.
http://steve.dynedge.co.uk/2010/09/19/multipart-body-a-gem-for-working-with-multipart-data/
With respect to rspec and paperclip, the has_attached_file :photo directive creates a virtual attribute of sorts i.e. :photo ... when you assign a file or a path to photo, paperclip takes over, stores the file, optionally does processing on it e.g. auto-create thumbnails, import a spreadsheet, etc. You aren't telling rspec to test paperclip. You are invoking code and telling rspec what the results of that code -should- be.
In $GEM_HOME/gems/paperclip-2.3.8/README.rdoc, about 76% of the way through the file under ==Post Processing (specifically lines 147 and 148):
---[ BEGIN QUOTE ]---
NOTE: Because processors operate by turning the original attachment into the styles, no processors will be run if there are no styles defined.
---[ END QUOTE ]---
Reading the code, you'll see support :original ... does your has_attached_file define a style?
I use a generic ":styles => { :original => { :this_key_and => :this_value_do_not_do_anything_unless_a_lib_paperclip_processors__foo_dot_rb__does_something_with_them } }" ... just to get paperclip to move the file from some temp directory into my has_attached_file :path
One would think that would be default or more obvious in the docs.