I just added validations for a carrierwave image to a model and now tests run really slow. How can I speed up this process? I feel like there must be a better way.
I've been running without validations and used to be able to run through my rspec tests in about 140 seconds, but since i now validate presence of :display_pic
I've had to add real file uploads to my project factory. This has upped it to 240 seconds! 140 was already on the heavy side, this is just crazy.
This is how the carrierwave github page recommends setting up Factory Girl:
FactoryGirl.define do
factory :project do
display_pic { File.open(File.join(Rails.root, 'spec', 'support', 'projects', 'display_pics', 'test.jpg')) }
end
end
I made the above test.jpg just an empty text file, so its essentially as small a file as possible.
I also followed the carrierwave recommendation to setup testing:
CarrierWave.configure do |config|
config.storage = :file
config.enable_processing = false
end
Are your uploads stored locally or are they going to a cloud service like S3? If the latter, that's probably what's killing your test performance.
For tests, it's always good practice to mock out any external dependencies. If this is your problem, you should use a tool to mock the connection. I think the fog gem comes with built-in support for this.
Update: Regarding the answer by Jefferson Girao, I've used a trick to avoid opening a test file and instead using StringIO to simulate tests files for factory purposes. This performs better because it avoids the disk access:
def test_file_stream(filename = 'test.jpg', mime_type='image/jpg', content = '')
StringIO.new(content).tap do |s|
s.content_type = mime_type
s.original_filename = filename
end
end
With validation happening now always that a instance is created the attribute display_pic is accessed and the code inside the brackets
{ File.open(File.join(Rails.root, 'spec', 'support', 'projects', 'display_pics', 'test.jpg')) }
will be executed (it is lazily executed). This is causing the difference in time.
An option to avoid this is to set to_create for the factory definition what i don't recommend:
FactoryGirl.define do
factory :project do
display_pic { File.open(File.join(Rails.root, 'spec', 'support', 'projects', 'display_pics', 'test.jpg')) }
to_create do |instance|
instance.save!(:validate => false)
end
end
end
With inspiration from @jeffersongirao and @Wolfram Arnold:
FactoryGirl.define do
sequence(:image) do |n|
{
tempfile: StringIO.new('{・㉨・}'),
filename: "#{n}.jpeg",
content_type: 'image/jpeg'
}
end
factory :user do
avatar { generate(:image) }
end
end
Two key things here:
CarrierWave's upload setters can make sense of a whole bunch of things. They do it by wrapping what they get in a CarrierWave::SanitizedFile
. One of the things it can take is a hash, as here.
CarrierWave::SanitizedFile
cares whether the file is empty. I spent way too long wondering why it wouldn't accept my StringIO.new
. It needs there to be something in there, but it doesn't care what. I gave it a koala.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With